Hi I'm Trying to work with setters and getters in python for one class and I want to use the setter with other instruction, so when setting the property value, the setter do more than only assign the value to the property. Maybe I'm confused on how the setter and getter work. Here is an example:
class test:
self.x = None
#property
def var(self):
return self.x
#var.setter
def var(self, value):
print("Assigning Value")
self.x = value
v = test()
v.var = "hello"
So what I'm trying to do in this example is to print "Assigning Value" when x value is assigned, but so far Isn't working.
My question is. Do I'm doing something wrong?,Do I miss something?, or that's not the way setters and getters work?
Thanks
Change
class test:
To:
class test(object):
In Python 2.x you can only use the descriptors if inheriting from object. (Its a backwards compatibility thing). Fortunately, this has been resolved in Python 3.x
At this point, there is no self:
class test:
self.x = None
# ^^^^^ BAD
change it to either set in the initializer:
class test:
def __init__(self):
self.x = None
or use a class attribute normally.
class test:
x = None
If you aren't sure which, go with the first; it'll confuse you less to use that pattern until you are more confident about the difference.
Related
I'm confused with how attribute variables are behaving in my class when using the property decorator.
See this example:
class Example:
def __init__(self, x):
self.x = x
#property
def x(self):
return self.__x
#x.setter
def x(self, x):
self.__x = x
This works fine, but how? The variable inside the setter property (self.__x) has not been "defined" in the constructor, so how can it be assigned a value?
Other stuff also works, for example, take the same class defined above and add a new member function to it:
def set_val_x(self):
self.__x = 8765
Again, using this function actually works, similar to the property.setter (but it's not using the property decorator).
Inside __init__, the line
self.x = x
is no longer short for
setattr(self, 'x', x)
because the class attribute Example.x exists. You are no longer creating an instance attribute x, but calling
type(self).X.__set__(self, 'x', x)
which will set the instance attribute __x.
Instance attributes can be created, modified, or deleted at any time. The __init__ method is just a convenient, single place to create them because it is called for you automatically every time you create an instance.
In Python, you don’t have to define variables in the constructor. You can assign them whenever you want.
class Foo:
def __init__(self, bar):
self.bar = bar
my_foo = Foo(3)
my_foo.other_thing = 6
is perfectly legal, for example.
In order not to extend myself too much I will give a basic and hypothetical example of what I am trying to do.
Suppose the following class:
class foo():
def __init__(self):
self.keywords = []
## this method returns the entire list
def get_keywords(self):
return self.keywords
def set_keywords(self, value):
self.keywords.append(value)
But I want to code this in a pythonic way using the #property decorator.
My (wrong) attempt to do this:
class foo:
def __init__(self):
self.key = []
#property
def key(self):
return self.__key
#key.setter
def key(self, value):
self.__key.append(value)
So, whats is wrong in my attempt ?
ps: English is not my native language and I hope my doubt is understandable.
In your original code, self.set_keywords only appends to an existing list; it does not let you initialize the value of keywords to an arbitrary list. This restriction is preserved in your property-based code, which means you cannot assign directly to self.key; you have to initialize the underlying list in __init__ directly.
class foo:
def __init__(self):
# self.key = [] is equivalent to `self.__key.append([])`, but
# self.__key doesn't exist yet. (And would be wrong even if it did.)
self.__key = []
#property
def key(self):
return self.__key
#key.setter
def key(self, value):
self.__key.append(value)
However, this means an assignment like self.key = 3 doesn't actually perform what most people would expect of an assignment. It doesn't overwrite the old value, it adds to it instead. Use the setter to provide a fixed list, but a different method to add to an existing one.
class foo:
def __init__(self):
self.__keys = []
#property
def keys(self):
return self.__keys
#keys.setter
def keys(self, values):
self.__keys = values
def add_key(self, value):
self.__key.append(value)
And finally, it's not necessarily more Pythonic to use a property if you don't actually do any sort of extra work or validation in the getter or setter. If all you are doing is wrapping access to an underlying value, just let the value be used directly.
class foo:
def __init__(self):
self.keys = []
self.keys = [1,2,3]
print(self.keys)
self.keys.append(4)
# etc
The nice thing about properties is that if you start by allowing direct access to keys, then nothing about how you use keys changes if you later decide to replace it with a property.
You can give this a try:
class Foo:
def __init__(self):
self._key = []
#property
def key(self):
return self._key
#key.setter
def key(self, value):
self._key = value
Here are my two cents:
Rename the class foo to Foo
You can't initialize self.key, as this is the property, so initialize the correct variable in the constructor (i.e. __init__)
Private vars are prefixed with one _ scope and not two (two __ are Python internals)
I suppose you rather want my_instance.key = ['spam', 'eggs'] to replace the foo._key value than extend it. Because this is kind of a "setter" and that would result in a weird behaviour, or at least another developer won't expect that behaviour from that setter/function
However, and that's important: As long as you're only doing this, you won't need properties. You can simply initialize self.keys in the constructor and froget about the property and setter function. Later on, when you want to change the behaviour, you can still add the property and setter. That's one reason why we've properties in Python, so that you won't have to refactor your whole code in case "a bit more logic" comes into place.
Btw. if you're really depending everything on those dict functions, you might also want to inherit your class from the dict class. Depends what you're up to.
Say I have a simple class Foo, which comes from an external library, thus I cannot change it directly:
class Foo(object):
def __init__(self, x):
self.x = x
I want to create a subclass Bar and prevent x from being change from an instance of Bar, but still use the x in Bar's methods.
Here's what I tried, and it will probably enlighten the basic idea, but unfortunately it doesn't work:
class Bar(Foo):
#property
def x(self):
return super().x
#x.setter
def x(self, value):
raise NotImplementedError('Do not change x directly, use "do_stuff()" instead')
def do_stuff(self, value):
if <something>:
super().x = value
So basically I've created some wrapper functions (do_stuff()) around an attribute, and now I want to prevent the attribute from being changed directly, as it might mess up some functionality of the wrapper functions. Is this possible in a reasonable way?
Edited with a better example of what I want. I'm not trying to prevent them from seeing the variable x, but instead changing it from outside of do_stuff()
This should be much simpler to accomplish if you are willing to avoid inheritance altogether:
def main():
bar = Bar(123)
bar.fizz()
bar.buzz()
bar.fizz()
bar.set_x(456)
print('bar.x =', bar.x)
try:
bar.x = 123
except AttributeError:
print('bar.x cannot be set directly')
else:
raise AssertionError('an AttributeError should have been raised')
bar.mutate_x(789)
bar.fizz()
bar.set_x(0)
bar.fizz()
bar.mutate_x(1)
bar.fizz()
bar.set_x('Hello World')
bar.fizz()
class Foo:
def __init__(self, x):
self.x = x
def fizz(self):
print(self.x)
def buzz(self):
self.x = None
class Bar:
def __init__(self, x):
self.__foo = foo = Foo(x)
self.__copy_methods(foo)
def __copy_methods(self, obj):
for name in dir(obj):
if name.startswith('__') or name.endswith('__'):
continue
attr = getattr(obj, name)
if callable(attr):
setattr(self, name, attr)
#property
def x(self):
return self.__foo.x
def set_x(self, value):
if isinstance(value, int) and value > 0:
self.__foo.x = value
mutate_x = set_x
if __name__ == '__main__':
main()
The short answer is: No, this is not possible in a reasonable way.
Python's guiding principle here, to use the phrasing from the style guide is that we are all responsible users. Meaning that code is trusted not to do silly things, and people should generally avoid messing with members of other people's classes without a good reason.
The first and best way to prevent people from accidentally changing a value is to mark it using the single underscore (_variable). This however may not offer you the protection you want against accidental modification of your variables.
The next step up in protection is to use a double underscore. Quoting from PEP-8:
To avoid name clashes with subclasses, use two leading underscores to invoke Python's name mangling rules.
Python mangles these names with the class name: if class Foo has an attribute named __a , it cannot be accessed by Foo.__a . (An insistent user could still gain access by calling Foo._Foo__a .) Generally, double leading underscores should be used only to avoid name conflicts with attributes in classes designed to be subclassed.
The mangling makes it more difficult to accidentally overwrite a value.
I added emphasis to that last sentence because it is important. Using this mechanism for preventing accidental access to a member is not really the something that should be done for a lot of members.
In your specific case, the way that I'd solve the problem would be to not subclass at all. Consider:
class Foo(object):
def __init__(self, x):
self.x = x
class Bar():
def __init__(self, x):
self._foo = Foo(x)
#property
def x(self):
return self._foo.x
def do_stuff(self, value):
# Validate the value, and the wrapped object's state
if valid:
self._foo.x = value
Of course this means that Bar has to wrap all of Foo's methods that you want to wrap. Yes, someone could still,
b = Bar(100)
b._foo.x = 127 # shame on them :)
or
b = Bar(100)
b._foo = EvilFoo(127)
but it's harder to unintentionally do.
You're on the right track, you want to make x a property instead of having it be an attribute in the subclass. Where you went wrong was trying to store the raw data for x on super. What you want to do is exploit the fact that the parent class can use the new property of the subclass transparently and does not need to know that it is now a property and not a attribute. Something like this should work for you:
class Foo(object):
def __init__(self, x):
self.x = x
class Bar(Foo):
_protected_x = None
#property
def x(self):
return self._protected_x
#x.setter
def x(self, value):
if self._protected_x is None:
self._protected_x = value
else:
raise ValueError("Use set_x to change x.")
def set_x(self, value):
self._protected_x = value
b = Bar(12)
print b.x
b.set_x(5)
print b.x
I can do:
class T(object):
i = 5
# then use the value somewhere in a function
def p(self):
print id(i), T.i
.. but, if I happen to subclass T ..
class N(T):
pass
.. then N.i will in fact be T.i. I found a way to deal with this:
class T(object):
i = 5
def p(self):
print self.__class__.i
.. is this correct and sure to work? Or can it produce unexpected behavior in some situations (which I am unaware of)?
self.__class__.i is correct and sure to work (although i is a poor naming choice).
if the method from which you access i does not use self, you can make it a class method, in which case the first parameter will be the class and not the instance:
class T(object):
i = 5
#classmethod
def p(cls):
print cls.i
To read the attribute, you can also use self.i safely too. But to change its value, using self.i = value will change the attribute of the instance, masking the class attribute for that instance.
Uh... did you know you can refer to class attributes from instances?
class T(object):
i = 5
def p(self):
print(id(self.i), self.i)
Class methods aside, I just thought of an interesting idea. Why not use a property that accesses the underlying class instance?
class T(object):
_i = 5
#property
def i(self):
return self.__class__._i
#i.setter(self, value)
self.__class__._i = value
Of course this wouldn't prevent users from utilizing an instance's _i seperate from the class's _i.
Consider the following class :
class Token:
def __init__(self):
self.d_dict = {}
def __setattr__(self, s_name, value):
self.d_dict[s_name] = value
def __getattr__(self, s_name):
if s_name in self.d_dict.keys():
return self.d_dict[s_name]
else:
raise AttributeError('No attribute {0} found !'.format(s_name))
In my code Token have some other function (like get_all() wich return d_dict, has(s_name) which tell me if my token has a particular attribute).
Anyway, I think their is a flaw in my plan since it don't work : when I create a new instance, python try to call __setattr__('d_dict', '{}').
How can I achieve a similar behaviour (maybe in a more pythonic way ?) without having to write something like Token.set(name, value) and get(name) each I want to set or get an attribute for a token.
Critics about design flaw and/or stupidity welcome :)
Thank !
You need to special-case d_dict.
Although of course, in the above code, all you do is replicate what any object does with __dict__ already, so it's pretty pointless. Do I guess correctly if you intended to special case some attributes and actally use methods for those?
In that case, you can use properties.
class C(object):
def __init__(self):
self._x = None
#property
def x(self):
"""I'm the 'x' property."""
return self._x
#x.setter
def x(self, value):
self._x = value
#x.deleter
def x(self):
del self._x
The special-casing of __dict__ works like this:
def __init__(self):
self.__dict__['d_dict'] = {}
There is no need to use a new-style class for that.
A solution, not very pythonic but works. As Lennart Regebro pointed, you have to use a special case for d_dict.
class Token(object):
def __init__(self):
super(Token,self).__setattr__('d_dict', {})
def __getattr__(self,name):
return self.a[name]
def __setattr__(self,name,value):
self.a[name] = value
You need to use new style classes.
the problem seems to be in time of evaluation of your code in __init__ method.
You could define __new__ method and initialize d_dict variable there instead of __init__.
Thats a bit hackish but it works, remember though to comment it as after few months it'll be total magic.
>>> class Foo(object):
... def __new__(cls, *args):
... my_cls = super(Foo, cls).__new__(cls, *args)
... my_cls.d_dict = {}
... return my_cls
>>> f = Foo()
>>> id(f.d_dict)
3077948796L
>>> d = Foo()
>>> id(d.d_dict)
3078142804L
Word of explanation why I consider that hackish: call to __new__ returns new instance of class so then d_dict initialised in there is kind of static, but it's initialised with new instance of dictionary each time class is "created" so everything works as you need.
It's worth remembering that __getattr__ is only called if the attribute doesn't exist in the object, whereas __setattr__ is always called.
I think we'll be able to say something about the overall design of your class if you explain its purpose. For example,
# This is a class that serves as a dictionary but also has user-defined methods
class mydict(dict): pass
# This is a class that allows setting x.attr = value or getting x.attr:
class mysetget: pass
# This is a class that allows setting x.attr = value or getting x.attr:
class mygetsethas:
def has(self, key):
return key in self.__dict__
x = mygetsethas()
x.a = 5
print(x.has('a'), x.a)
I think the last class is closest to what you meant, and I also like to play with syntax and get lots of joy from it, but unfortunately this is not a good thing. Reasons why it's not advisable to use object attributes to re-implement dictionary: you can't use x.3, you conflict with x.has(), you have to put quotes in has('a') and many more.