Python 3.x - Attribute descriptor using setattr/getattr vs internal value - class

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 :)

Related

How to change Class object name in a loop

So I have Class
class MyClass
....do things...
and I add objects to it with
ObjName = MyClass(things)
and my problem is that when I add ObjName to MyClass in a loop, I can't figure out a way to create a new object name each loop so it keeps overwriting the only Obj this ends up creating. I tried adding a list as in
ObjName[i] = MyClass(things)
but it wouldn't work.
Here is what I'm trying to do specifically (edited for clarity):
So when I add objects to MyClass, the name of the object added should be callable with input, like so:
somename = input("objname: ") # User input decides how the object values can be called
TempObjName = MyClass(things) # Values of the specific object, will contain more than one unique object
*...*
somename.someattribute() ## 2 different
somename2.someattribute() ## values, sets or etc
Try this:
ObjList = []
for i in whatever:
temp = MyClass(things)
ObjList.extend(temp)
I wasn't able to create the function I wanted per-say, but because my code had a dictionary that saved the obj name and obj value in it, I was able to do what I initially wanted to do by rerunning the requested name from the dictionary in the Class.

Trouble understanding private attributes in classes and the class property method in Python 3

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.

plone.formwidget - Is it possible to set a MasterSelect Field as an AutocompleteFieldWidget?

I am trying to set a MasterSelect field to an AutocompleteFieldWidget.
I'm using AutocompleteFieldWidget from plone.formwidget.autocomplete and the MasterSelectField from plone.formwidget.MasterSelect. The slave field belonging to the MasterSelectField is also a MasterSelectField.
The autocomplete functions as it should (retrieving the values based on input), but the slave field's choices do not change. However, when its not set as an autocomplete, everything works as it should.
Edit:
In my buildout-cache, I looked at widget.py in plone.formwidget.masterselect and tried placing a print statement in getSlaves and that function wasn't getting called. I tried the render function and that wasn't getting called either. Then I placed a print statement in MasterSelectField and that was notgetting called. Setting the field to an Autocomplete widget removes any trace that its a Master Select field.
Edit: In the init.py file in plone.formwidget.masterselect, I placed a print statement in the init function of the MasterSelectField, and the slave widget does print, where as in getSlaves in widget.py it doesn't. This is the output I'm getting from printing in the init and what I should be getting in getSlaves:
({'action': 'vocabulary', 'masterID': 'form-widgets-IMyForm-master_field',
'control_param': 'master_value', 'name': 'IMyForm.slave_field',
'vocab_method': <class 'my.product.vocabulary.SlaveVocab'>},)
I have my interface:
from plone.directives import form
class IMyForm(model.Schema):
form.widget(master_field=AutocompleteFieldWidget)
master_field = MasterSelectField(
title=_(u'Master'),
slave_fields=({'name':'IMyForm.slave_field',
'action':'vocabulary',
'source':MySource,
'control_param':'master_value'
}),
required=True,
)
slave_field = MasterSelectField(title=_(u'Slave Field'),
source=SlaveVocab,
slave_fields=(....
)
required=False,
)
I have my source object for the master field:
class MySource(object):
implements(IQuerySource)
def __init__(self, context):
simple_terms = []
#Query portal catalog for unique indexes, and fill with simple terms
self.vocab = SimpleVocabulary(simple_terms)
def __contains__(self, term):
return self.vocab.__contains__(term)
def getTermByToken(self, token):
return self.getTermByToken(token)
def getTerm(self, value):
return self.getTerm(value)
def search(self, query_string):
return [term for term in self.vocab if query_string in term.title.lower()]
class MySourceBinder(object):
implements(IContextSourceBinder)
def __call__(self, context):
return MySource(context)
My slave field's source is:
class SlaveVocab(object):
grok.implements(IContextSourceBinder)
def __init__(self, **kw):
self.master_value = kw.get('master_value', None)
def __call__(self, context):
if self.master_value is None or self.master_value == "--NOVALUE--"
self.master_value = getattr(context,'master_field',None)
#Still nothing, return empty vocabulary
if self.master_value is None or self.master_value == '--NOVALUE--':
return SimpleVocabulary([])
terms = []
#If not null, building a simple vocabulary to return
return SimpleVocabulary(terms)
I did a print statement in call of the Slave Vocabulary and it was being called, but nothing was being passed in.
I also tried using another widget, ChosenFieldWidget. I get the same results in that it functions as it should, but the slave field's choices do not change. Is it possible to set a master select field to an autocomplete? If so, what am I doing wrong?
Also, I'm using Solgema.fullcalendar and the content type extends the IEventBasic behavior, so I don't have access to using my own form class I would've liked to have used since Solgema seems to render its own forms.
Edit:
I am using Plone 4.3

Supporting "recursive objects" in lua

I'm fairly new to lua and have the following problem with an assignment from a class:
We currently extend lua to support objects and inheritance. The Syntax for that is
Class{'MyClass',
attribute1 = String,
attribute2 = Number
}
Class{'MySubClass', MyClass,
attribute3 = Number
}
This works perfectly fine. The real problem lies within the next task: We should support "recursive types", that means a call like
Class{'MyClass', attribute = MyClass}
should result in an class with a field of the same type as the class. When this "class-constructor" is called the variable MyClass is nil, thats why the parameter table doesnt't have an entry attribute. How is it possible to access this attribute?
My first thought was using some kind of nil-table which gets returned every time the global __index is called with an unset key. This nil-table should behave like the normal nil, but can be checked for in the "class-constructor". The problem with this approach are comparisons like nil == unknown. This should return true, but as the __eq meta method of the nil-table is never called we cannot return true.
Is there another approach I'm currently just ignoring? Any hint is greatly appreciated.
Thanks in advance.
Edit:
Here the relevant part of the "testfile". The test by which the code is rated in class is another one and gets published later.
three = 3
print( three == 3 , "Should be true")
print( unknown == nil , "Should be true" )
Class{'AClass', name = String, ref = AClass}
function AClass:write()
print("AClass:write(), name of AClass:", self.name)
end
aclass = AClass:create("A. Class")
aclass:write()
Since MyClass is just a lookup in the global table (_G), you could mess with its metatable's __index to return a newly-defined MyClass object (which you would later need to fill with the details).
However, while feasible, such an implementation is
wildly unsafe, as you could end up with an undefined class (or worse, you may end up inadvertantly creating an infinite lookup loop. Trust me, I've been there)
very hard to debug, as every _G lookup for a non-existing variable will now return a newly created class object instead of nil (this problem could somewhat be reduced by requiring that class names start with an uppercase character)
If you go that route, be sure to also override __newindex.
How about providing the argument in string form?
Class{'MyClass', attribute = 'MyClass'}
Detect strings inside the implementation of Class and process them with _G[string] after creating the class
Or alternatively, use a function to delay the lookup:
Class{'MyClass', attribute = function() return MyClass end}

Modifying a variable class attribute

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 )