I'm trying to modify a class attribute based on the argument given. I'm just getting into python but I can't seem to find a way to do it without using a dictionary. Is there a pythonic way to do this? See example below
class Ship:
def __init__(self, name):
self.name = name
ship_type = {"schooner": [50, 30, 18],
"galleon": [30, 14, 14]
}
self.max_weight = ship_type[name][0]
self.speed = ship_type[name][1]
self.poopdeck = ship_type[name][2]
def upgrade(self, attribute, value):
self.attribute += value
Someship.ship.upgrade(speed, 10)
I can write out a different method for each attribute but I feel as if there has to be something like this.
I apologize in advance if this has already been answered but I couldn't word it right if there is.
Change the update method to update an existing attribute by using the builtin functions hasattr(), setattr() and getattr().
def upgrade(self, attribute, value):
if hasattr(self, attribute):
setattr(self, attribute, getattr(self, attribute) + value )
else:
raise AttributeError("Can't upgrade non-existent attribute '{}'.".format(attribute))
Note that I'd also use the __dict__ attribute to make setting up your instances easier:
class Ship:
# types is a class variable, and will be the same for all instances,
# and can be referred to by using the class. ie `Ship.types`
types = {
"schooner": {'weight':50, 'speed':30, 'poopdeck':18},
"galleon": {'weight':30, 'speed':14, 'poopdeck':14},
"default": {'weight':11, 'speed':11, 'poopdeck':11}
}
def __init__(self, name):
self.name = name
# we update the instance dictionary with values from the class description of ships
# this means that instance.speed will now be set, for example.
if name in Ship.types:
self.__dict__.update(Ship.types[name])
else:
self.__dict__.update(Ship.types["default"])
def upgrade(self, attribute, value):
if hasattr(self, attribute):
setattr(self, attribute, getattr(self, attribute) + value )
else:
raise AttributeError("Can't upgrade non-existent attribute '{}'.".format(attribute))
ship = Ship("schooner")
print(ship.speed) #=> 30
ship.upgrade("speed", 10)
print(ship.speed) #=> 40
You are looking for the setattr and getattr functions. Your upgrade method can be implemented as
def upgrade(self, attribute, value):
setattr(self, attribute, getattr(self, attribute) + value )
Related
I have a table view. When I update the properties of one row, I can not see the modifications? For example:
implicit class PersonView(p:Person) {
val fname = new ObjectProperty(this, "fname",p.name)
}
and in my table view
lazy val tableLines = ObservableBuffer(persView)
val personTable = new TableView[PersonView](tableLines) {
columns ++= List(
new TableColumn[PersonView, String] {
text = "Name"
cellValueFactory = _.value.fname
cellFactory = { _ =>
new TableCell[PersonView, String] {
item.onChange { (_, _, newValue) => text = newValue }
}
}
}
)
}
It works fine, but when I update the name, I can not see that in GUI.
Firstly, I'll attempt to summarize what I'm seeing, and how I think you might get this to work:
The PersonView class decorates a Person instance by providing an fname property, that is initialized to the name field of the associated Person. When creating each cell in the "Name" column, you create such a property and associate it with the value of the cell. Henceforth, whenever the value of that property changes, the cell will automatically change its item field to show the new value of that property. (BTW, the onChange property is redundant and unnecessary—it provides an opportunity to perform some other actions when the item property—that is, the bound fname property—changes, so the cell will have already been updated when it executes.)
So, if you now change the name of a Person instance, what happens to the cell for that Person in the "Name" column? Nothing.
Why?
Firstly, as #James_D points out, you have not established a relationship between the name of a Person instance, and the value of the ObjectProperty instance originally associated with it. That is, all you've done is change a String value. For the GUI to be updated, the value of that ObjectProperty needs to change too.
Adding to your problem is the fact that there is no relationship from the Person to its associated PersonView. So, when the Person name field is changed, there's no way for the Person to person to notify its PersonView. Worse, by making PersonView an implicit class, you're suggesting that PersonView instances themselves are unimportant and transient, existing temporarily solely to decorate some Person instance with an additional set of methods and/or properties.
So, how can we change things so that they work as you might expect? There are two basic approaches, and your choice will depend upon how much control you can exert on the Person class. The key in both cases is to ensure that the StringProperty (a better option than an ObjectProperty, incidentally) containing the name of the Person changes whenever the name of the Person is changed...
Firstly, the simplest method is to do away with PersonView class altogether. Clearly, you'll need to be able to edit Person to do this; if you cannot, you'll have to try the second approach. Person should be modified to add an fname property field, with name being converted to a function that reports the current value of fname:
// initName is the initial name of the Person, and may be changed later...
class Person(initName: String, /*Whatever other arguments you require*/) {
// String property storing this Person's name. Name is initialized to initName.
val fname = new StringProperty(this, "fname", initName)
// Report the current name of this Person.
def name = fname.value
// This function is not necessary, since we could change the value through fname directly
// but it does look better...
def name_=(newName: String): Unit = fname.value = newName
}
In this case, your table initialization now looks like this:
val tableLines = ObservableBuffer(persView) // Of Person, not PersonView!
val personTable = new TableView[Person](tableLines) {
columns ++= List(
new TableColumn[Person, String] {
text = "Name"
cellValueFactory = _.value.fname
// No need for a cellFactory - default works fine.
}
)
}
Now, you can change the name of a Person like this:
val someone = new Person("Bob"/*, etc...*/)
someone.name = "Fred"
And all is good. The fname property, the name field and the value of the corresponding cell in the GUI table, will now all have the same value.
The second approach is required if you cannot modify the definition of the Person type. Here, we use PersonView to change the names of Person instances, and hope that no-one changes Person names outside of our control. (That is, if some other code modifies the name of a Person instance without going through PersonView, then we'll know nothing about it, and the GUI will not be updated accordingly.)
PersonView, in this case, must not be an implicit class. We want to retain a PersonView instance and use it to interact with an associated Person instance. PersonView now looks like this:
class PersonView(p: Person) {
// String property initialized to the name of the associated person.
val fname = new StringProperty(this, "fname", p.name)
// Change the name of the person. Note that we MUST also change the name of the
// associated person instance.
def name_=(newName: String): Unit = {
// Change the name of the Person instance. Verify it has the value we think it has.
assert(p.name == fname.value)
p.name = newName // Might be p.setName(newName), etc. in your case
// Change the name of our property.
fname.value = newName
}
}
Now, say you have a list of Person instances, you'll need to map them to PersonView instances, and use those latter instances subsequently.
Your GUI code now looks like this:
val tableLines = ObservableBuffer(persView)
val personTable = new TableView[PersonView](tableLines) {
columns ++= List(
new TableColumn[PersonView, String] {
text = "Name"
cellValueFactory = _.value.fname
// No need for a cellFactory - default works fine.
}
)
}
Changing the names of people is now a little more complex, because we need to be able to find the right PersonView instance, but it would look like this:
val someone = new Person("Bob"/*, etc...*/)
val someoneView = new PersonView(someone)
someoneView.name = "Fred"
And all is good once again. The PersonView.fname property, the Person.name field and the value of the corresponding cell in the GUI table (once someoneView is added to the tableLines observable), will now all have the same value.
However, the following line just changes the name of a Person instance. The PersonView and GUI do not get updated:
someone.name = "Eric"
This class example was taken from here.
class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
def get_temperature(self):
print("Getting value")
return self._temperature
def set_temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
print("Setting value")
self._temperature = value
temperature = property(get_temperature, set_temperature)
The idea here is that when we create an instance of Celsius and set the temperature attribute (e.g. foo = Celsus (-1000) ), we want to make sure that the attribute is not less than -273 BEFORE setting the temperature attribute.
I don't understand how it seems to bypass self.temperature = temperature and go straight to the last line. It seems to me like there are three attributes/properties created here: the Class attribute, temperature; the Instance attribute, temperature; and the set_temperature function which sets the attribute _temperature.
What I DO understand is that the last line (the assignment statement) must run the code property(get_temperature, set_temperature) which runs the functions get_temperature and set_temperature and intern sets the private attribute/property _temperature.
Moreover, if I run: foo = Celsius(100) and then foo.temperature, how is the result of foo.temperature coming from temperature = property(get_temperature, set_temperature) and thus _temperature AND NOT self.temperature = temperature? Why even have self.temperature = temperature if temperature = property(get_temperature, set_temperature) gets ran every time the foo.temperature call is made?
More questions...
Why do we have two attributes with the same name (e.g. temperature) and how does the code know to retrieve the value of _temperature when foo.temperature is called?
Why do we need private attributes/properties an not just temperature?
How does set_temperature(self, value) obtain the attribute for parameter value (e.g. the argument that replaces value)?
In short, please explain this to me like a three year old since I have only been programming a few months. Thank you in advance!
When we are first taught about classes/objects/attributes we are often told something like this:
When you look up an attribute like x.foo it first looks to see if
'foo' is an instance variable and returns it, if not it checks if
'foo' is defined in the class of x and returns that, otherwise an
AttributeError is raised.
This describes what happens most of the time but does not leave room for descriptors. So if you currently think the above is all there is about attribute lookup property and other descriptors will seem like an exception to these rules.
A descriptor basically defines what to do when looking up / setting an attribute of some instance, property is an implementation that lets you define your own functions to call when getting / setting / deleting an attribute.
When you do temperature = property(get_temperature, set_temperature) you are specifying that when x.temperature is retrieved it should call x.get_temperature() and the return value of that call will be what x.temperature evaluates to.
by specifying set_temperature as the setter of the property it states that when ever x.temperature is assigned to something it should call set_temperature with the value assigned as an argument.
I'd recommend you try stepping through your code in pythontutor, it will show you exactly when get_temerature and set_temperature are called after which statements.
I am new to Python descriptors. The question below is to help me better understand how they work.
In Python 3.x, I am using an attribute descriptor. The particular thing about this descriptor it that its set method contains a lot of sanity checks to make sure the value about to be set to the attribute respects some rules. The constructor uses the setattr and getattr to manipulate the attribute. The constructor works well and its code is reported below.
class AttributeDescriptor(): <----- Version 001 of this class
def __init__(self, attname):
self.__attname = "__" + attname
def __set__(self, obj, attvalue):
#Some data quality checks, not provided here...
setattr(obj, self.__attname, attval)
def __get__(self, obj, owner):
return getattr(obj, self.__attname)
class Hobbit():
def __init__(self):
pass
name = AttributeDescriptor("name")
sam = Hobbit()
merry = Hobbit()
sam.name = "Sam"
merry.name = "Merry"
print(sam.name) ----> Returns "Sam"
print(merry.name) ----> Returns "Merry"
print(sam.name) ----> Returns "Sam"
I also tried defining the constructor with the following code, which returned erroneous values for the "name" attribute. Indeed, all Hobbits names were set equal to the last name which had been defined.
class AttributeDescriptor(): <---- Version 002 of this class
def __set__(self, obj, attvalue):
#Some data quality checks, not provided here...
self.value = attvalue
def __get__(self, obj, owner):
return self.value
class Hobbit():
def __init__(self):
pass
name = AttributeDescriptor()
sam = Hobbit()
merry = Hobbit()
sam.name = "Sam"
merry.name = "Merry"
print(sam.name) ----> Returned "Merry"
print(merry.name) ----> Returned "Merry"
print(sam.name) ----> Returned "Merry"
My question is: how come the descriptor Version 002 sets "name" equal to a common value through all its istances ?
From what I understand of descriptors, the descriptor Version 001 will store names in an attribute of the Person object instance:
sam.__name = "sam"
merry.__name = "merry"
while the descriptor Version 002 will store names in an attribute of the attribute of the Person object instance:
same.name.value = "sam"
merry.name.value = "merry"
Therefore, there is obviously something which I do not understand about how a Python descriptor works. Could anyone provide me with some clarifications ?
In your second example your are setting the value on the AttributeDescriptor instance itself.
You have only one AttributeDescriptor() instance in your program and there for it changes the same value every time you get to his set method
class Hobbit():
def __init__(self):
pass
name = AttributeDescriptor("name") # <---- happens ONLY once!!!
The class definition in python happens only* once ...
*unless it doesn't. But lets stick to the easier Truth :)
I have a Report class that several models inherit from (OverviewReport, CategoryReport, etc...). Each of these inherited classes has specific methods/attributes that need to be customized for that type of report.
The desired report type is passed in via the params hash, so I can do something like the following:
# reports_controller.rb
def index
case params[:type]
when "overview" then OverviewReport.new(...)
when "category" then CategoryReport.new(...)
...etc...
end
end
This works, but I would like to clean up the controller a little bit. I would like to be able to do this:
# reports_controller.rb
def index
#report = Report.new(params[:type], ...)
end
# report.rb class
def initialize(type, options)
case type
when "overview" then self = OverviewReport.new(type, options)
when "category" then self = CategoryReport.new(type, options)
end
end
However, you can't change the value of self, so how would you go about accomplishing this functionality?
The intent is the clean up the controller code and abstract away which report you're using, so I can call #report.some_method() and it will call the inherited-specific method.
Is there a clean solution to this, or am I stuck with a (somewhat) lengthy case statement in my controller?
One solution would be to use a "factory" class whose sole purpose is to instantiate the correct class:
# reports_controller.rb
def index
#report = AgnosticReport.new(type)
end
# agnostic_report.rb
def initialize(type)
case type
when "overview" then return OverviewReport.new(type)
when "category" then return CategoryReport.new(type)
...etc...
end
end
This is perfectly acceptable, but I was wondering if there was any need to add a "third" layer of classes to this situation.
Do you have the ability to change the type params passed?
If so I would use constantize
params[:type] being "Overview Report" or "Category Report" e.t.c.
then use
#report = params[:type].constantize.new
s = "OverviewReport",
s.constantize => OverviewReport
I have a custom class module in VBA (Access) that is supposed to handle a large amount of external data. Currently I have two functions Read(name) and Write(name, value) that allows to read and set dynamic properties.
Is there a way to define a more syntactic way to read and write those data? I know that some objects in VBA have a special way of accessing data, for example the RecordSet, which allows to read and set data using myRS!property_name. Is there a way to do exactly the same for custom class modules?
The exclamation mark syntax is used to access members of a Scripting.Dictionary instance(you'll need to add a reference to Microsoft Scripting Runtime through Tools > References first). To use this syntaxyou'll need to be storing the information internally in a dictionary.
The quickest way to use it in a class is to give your class an object variable of type Scripting.Dictionary and set it up as follows:
Option Explicit
Dim d As Scripting.Dictionary
Private Sub Class_Initialize()
Set d = New Scripting.Dictionary
End Sub
Private Sub Class_Terminate()
Set d = Nothing
End Sub
Public Property Get IntData() As Scripting.Dictionary
Set IntData = d
End Property
Now you can access properties using myinstance.IntData!MyProperty = 1... but to get to where you want to be you need to use Charlie Pearson's technique for making IntData the default member for your class.
Once that's done, you can use the following syntax:
Dim m As MyClass
Set m = New MyClass
Debug.Print "Age = " & m!Age ' prints: Age =
m!Age = 27
Debug.Print "Age = " & m!Age ' prints: Age = 27
Set m = Nothing
Okay, thanks to Alain and KyleNZ I have now found a working way to do this, without having a collection or enumerable object below.
Basically, thanks to the name of the ! operator, I found out, that access via the bang/pling operator is equivalent to accessing the default member of an object. If the property Value is the default member of my class module, then there are three equivalent statements to access that property:
obj.Value("param")
obj("param")
obj!param
So to make a short syntax working for a custom class module, all one has to do is to define a default member. For example, I now used the following Value property:
Property Get Value(name As String) As String
Value = SomeLookupInMyXMLDocument(name)
End Property
Property Let Value(name As String, val As String) As String
SetSomeNodeValueInMyXMLDocument(name, val)
End Property
Normally, you could now access that like this:
obj.Value("foo") = "New value"
MsgBox obj.Value("foo")
Now to make that property the default member, you have to add a line to the Property definition:
Attribute Value.VB_UserMemId = 0
So, I end up with this:
Property Get Value(name As String) As String
Attribute Value.VB_UserMemId = 0
Value = SomeLookupInMyXMLDocument(name)
End Property
Property Let Value(name As String, val As String) As String
Attribute Value.VB_UserMemId = 0
SetSomeNodeValueInMyXMLDocument(name, val)
End Property
And after that, this works and equivalent to the code shown above:
obj("foo") = "New value"
MsgBox obj("foo")
' As well as
obj!foo = "New value"
MsgBox obj!foo
' Or for more complex `name` entries (i.e. with invalid identifier symbols)
obj![foo] = "New value"
MsgBox obj![foo]
Note that you have to add the Attribute Value.VB_UserMemId = 0 in some other editor than the VBA editor that ships with Microsoft Office, as that one hides Attribute directives for some reason.. You can easily export the module, open it in notepad, add the directives, and import it back in the VBA editor. As long as you don't change too much with the default member, the directive should not be removed (just make sure you check from time to time in an external editor).
See this other question: Bang Notation and Dot Notation in VBA and MS-Access
The bang operator (!) is shorthand for
accessing members of a Collection or
other enumerable object
If you make your class extend the Collection class in VBA then you should be able to take advantage of those operators. In the following question is an example of a user who extended the collection class:
Extend Collections Class VBA