I'm working with a legacy system for which a Python interface has been added recently.
In my code, I get messages containing ASCII strings for attributes to be set in some wrapper classes.
I would like to use a dictionary to map "data labels" to property setter methods. Each property setter would be used as a "callback" when the corresponding data label is encountered in a message.
Using explicit setters/getters, the essential logic looks like this:
class A():
def __init__(self):
self._x = 1.2
def get_x(self):
return self._x
def set_x(self, value):
self._x = value
myA = A()
myTable = {
'X' : myA.set_x,
}
label, value = get_message()
print(myA.get_x())
# label is 'X', value a float
myTable[label](value)
print(myA.get_x())
This works, but is a bit ugly. I would like to use the #property decorator, but then I don't know how to reference the setter method in the dictionary.
I.e. the following doesn't work.
class B():
def __init__(self):
self._x = 1.2
#property
def x(self):
return self._x
#x.setter
def x(self, value):
self._x = value
myB = B()
myTable = {
'X' : myB.x
}
label, value = get_message()
print(myB.x)
# doesn't work as expected
myTable[label] = value
# no change
print(myB.x)
Of course, the reference to property myB.x in the dictionary definition calls the getter, so a float value is associated to the 'X' key. The myTable[label] = value assignment just replaces this value, it doesn't call the setter.
So, is there a way to get a reference to the property setter to insert in the dictionary and to later invoke as a "callback"?
I dug in reference information and this answer, but can't figure out a solution by myself.
Or, am I getting it wrong and I should follow a different path? (Suggestions welcome).
To access the actual function, you have to access the property directly on the class, so:
In [1]: class B:
...: def __init__(self):
...: self._x = 1.2
...:
...: #property
...: def x(self):
...: return self._x
...:
...: #x.setter
...: def x(self, value):
...: self._x = value
...:
In [2]: B.x.fset
Out[2]: <function __main__.B.x(self, value)>
Since functions are descriptors, you can use their __get__ method to bind them and change them into a method:
In [4]: B.x.fset.__get__(b)(42)
In [5]: b.x
Out[5]: 42
So, something like:
In [6]: my_table = {'X':B.x.fset.__get__(b)}
In [7]: my_table['X']('foo')
In [8]: b.x
Out[8]: 'foo'
I'm coming to this several years late, but I have a similar situation, and building off of juanpa.arrivillaga's answer I came up with this to answer your follow-up question, which is maybe what you were really hoping for originally.
Basically, an instance of TestDevice can use its own class method and getattr to find and call the appropriate setter:
class TestDevice(object):
#classmethod
def set_property(cls, instance, property, value):
getattr(cls, property).fset(instance, value)
def __init__(self, x):
self.x = x
def apply_state(self, state):
for k, v in state.items():
self.set_property(self, k, v)
#property
def x(self):
return self._x
#x.setter
def x(self, v):
self._x = v
Which seems to do the trick:
>>> thing = TestDevice(5)
>>> thing.x
5
>>> new_state = {'x': 7}
>>> thing.apply_state(new_state)
>>> thing.x
7
Related
I trying to implement a class property where the setter can only be called once and am wondering how best to achieve this ? And how to make it most 'pythonic'?
Options I have considered:
Subclass and extend the builtin property.
Decorate the setter of a property.
Add an attribute which persists how often each setter has been set.
Any other ideas?
And suggestions how to best implement?
If you are using it a lot, along with other property functionality, subclassing property is appropriate.
It is a bit tricky, due to the way property works - when one calls
#prop.setter, a new instance of the property is created. The subclass bellow will work.
class FuseProperty(property):
def setter(self, func):
def fuse(instance, value):
name = f"_fuse_{self.fget.__name__}"
if not getattr(instance, name, False):
func(instance, value)
setattr(instance, name, True)
return super().setter(lambda instance, value: fuse(instance, value))
Here is it in use.
In [24]: class A:
...: #FuseProperty
...: def a(self):
...: return self._a
...: #a.setter
...: def a(self, value):
...: self._a = value
...:
In [25]: a = A()
In [26]: a.a = 23
In [27]: a.a
Out[27]: 23
In [28]: a.a = 5
In [29]: a.a
Out[29]: 23
However, if this "fuse" property is all you need, and no other code is being added to the getters and setters, it can be much simpler: you can create a brand new "Descriptor" class, using the same mechanisms used by property - this can be much better, as your "fuse" properties can be built in a single line, with no need for a setter and getter methods.
All that is needed is a class with __get__ and __set__ methods - we can add __set_name__ to get the new property name automatically (which property itself does not, so we get the name from the fget method above)
class FuseAttribute:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
if instance is None:
return self
return getattr(instance, f"_{self.name}")
def __set__(self, instance, value):
if not getattr(instance, f"_fuse_{self.name}", False):
setattr(instance, f"_{self.name}", value)
# add an else clause for optionally raising an error
setattr(instance, f"_fuse_{self.name}", True)
And using it:
In [36]: class A:
...: a = FuseAttribute()
...:
In [37]: a = A()
In [38]: a.a = 23
In [39]: a.a
Out[39]: 23
In [40]: a.a = 5
In [41]: a.a
Out[41]: 23
Properties in Python are just descriptors, and it's relatively easy to implement your own that does exactly what you want:
class SetOnceProperty:
def __init__(self, name):
self.storage_name = '_' + name
def __get__(self, obj, owner=None):
return getattr(obj, self.storage_name)
def __set__(self, obj, value):
if hasattr(obj, self.storage_name):
raise RuntimeError(f'{self.storage_name[1:]!r} property already set.')
setattr(obj, self.storage_name, value)
def __delete___(self, obj):
delattr(obj, self.storage_name)
class Test:
test_attr = SetOnceProperty('test_attr')
def __init__(self, value):
self.test_attr = value*2 # Sets property.
test = Test(21)
print(test.test_attr) # -> 42
test.test_attr = 13 # -> RuntimeError: 'test_attr' property already set.
I have often preferred this way; "explicit is better than implicit":
class MyError(Exception):
...
NOT_SET = object()
class C:
def set_my_property(self, spam, eggs, cheese):
"""This sets the property.
If it's already set, you'll get an error. Donna do dat.
"""
if getattr(self, "_my_property", NOT_SET) is NOT_SET:
self._my_property = spam, eggs, cheese
return
raise MyError("I said, Donna do dat.")
#property
def my_property(self):
return self._my_property
Testing:
c=C()
c.set_my_property("spam", "eggs", "cheese")
assert c.my_property == ("spam", "eggs", "cheese")
try:
c.set_my_property("bacon", "butter", "coffee")
except MyError:
pass
This is simple enough and more general. A decorator for a function to be called only once, and ignore subsecuent calls.
def onlyonce(func):
#functools.wraps(func)
def decorated(*args):
if not decorated.called:
decorated.called = True
return self.func(*args)
decorated.called = False
return decorated
use like this
class A:
#property
def x(self):
...
#x.setter
#onlyonce
def x(self, val):
...
Or you can define a descriptor:
class Desc:
def __get__(self, inst, own):
return self._value
def __set__(self, inst, value):
if not hasattr(self, _value):
self._value = value
And use like this:
class A:
x = Desc()
It sounds like the attrs project would be helpful for your use case. You can realize "frozen" attributes in the following way:
import attr
#attr.s
class Test:
constant = attr.ib(on_setattr=attr.setters.frozen)
test = Test('foo')
test.constant = 'bar' # raises `attr.exceptions.FrozenAttributeError`
Note that it also supports validators via #constant.validator (see the example at the end of attr.ib docs).
How do you access the "private" variable behind a Python property?
In the following code, I get Hello World instead of the World World that I expect. I would guess that the property has a different scope that results in self being different in the property than the module. If so, is there a way to access the underlying variable? Or is there a different idiom to use for "code outside the class should use a different getter than code in the class"?
class Foo():
def __init__(self):
self._x = 'Hello'
self.x = 'World'
#property
def x(self):
return self._x
#x.setter
def x(self, val):
self._x = val
foo = Foo()
print(foo._x)
print(foo.x)
You forgot to inherit from object, so you created an old-style class. property setters don't work for old-style classes. If you inherit from object, you get World from both prints, like you expected:
>>> class Foo(object):
... def __init__(self):
... self._x = 'Hello'
... self.x = 'World'
... #property
... def x(self):
... return self._x
... #x.setter
... def x(self, val):
... self._x = val
...
>>> foo = Foo()
>>> print foo._x
World
>>> print foo.x
World
You are accessing the private variable _x via foo._x.
What is typical in this situation is to not define the x attribute at all, i.e. remove the line self.x = 'World'. That way you are using the property x to access the attribute _x (and because privates aren't really private you can of course also access the attribute directly using _x).
Edit: the first part of my answer was misleading, see user2357112's answer for why you are not getting the expected output.
Ok, lets say I have a really simple class i.e.:
class Test(object):
pass
What I would like to do is to define some default setter and getter methods
which are automatically applied to a new object member at creation time. In the example below a.x should always be uppercase, i.e.:
a = Test()
a.x = "foo"
print a.x
>>> FOO
If I create x within the class I would get this behavior like this:
class Test(object):
def __init__(self):
self._x = ""
#property
def x(self):
return self._x
#x.setter(self, string):
self._x = string.upper()
So is there any possibility to do this without defining setter and getter methods for each member ?? Thank a lot.
EDIT: With creation time I meant the creation time of a.x not of the class instance.
The simplest way is probably to override __setattr__, and change any string values to uppercase:
>>> class Test(object):
def __setattr__(self, attr, val):
if isinstance(val, basestring):
val = val.upper()
super(Test, self).__setattr__(attr, val)
>>> t = Test()
>>> t.x = 'foo'
>>> t.x
'FOO'
Subclass a dict;
In [1]: %cpaste
Pasting code; enter '--' alone on the line to stop or use Ctrl-D.
:class Struct(dict):
: """A dict subclass where you can simply use a dot to access attributes."""
:
: def __getattr__(self, name):
: return self[name]
:
: def __setattr__(self, name, value):
: self[name] = value
:--
In [2]: a = Struct()
In [3]: a.x = "foo"
In [4]: a.x
Out[4]: 'foo'
In [5]: a.length = 14
In [6]: a
Out[6]: {'length': 14, 'x': 'foo'}
That sounds like a use-case for pythons Descriptor Proctocol.
class WithDescriptors:
x = UpperCaseDescriptor()
y = UpperCaseDescriptor()
z = UpperCaseDescriptor()
class UperCaseDescriptor(object):
def __init__(self):
self.val = ''
def __get__(self, obj, objtype):
return self.val.upper()
def __set__(self, obj, val):
self.val = val
Thats just an outline and i didnt test the code to work!
If you want to extend such behaviour to every attribute of an instance,
even which are not existent, you should consider metaclasses.
This question already has answers here:
Understanding __get__ and __set__ and Python descriptors
(8 answers)
Closed 8 years ago.
Is there a function such that i can write but as a function?
class foo:
def __init__(self,x):
self.x = x;
asd = foo(2);
asd.x = 5;
print(asd.x);
But like:
class foo:
def __init__(self,x):
self.x = x;
def someFunction(self,string,value):
if(string == 'x'):
self.x = value;
print("worked");
asd = foo(2);
asd.x = 3; #and "worked" will be printed?
I tried __ set __ and __ setattr __ but i had no luck ;\
Is there a way to call a function when setting a class variable?
asd.x = 3; calls a function?
Use a property. The method decorated by #property will be used whenever you try to access the attribute x; the method subsequently decorated by #x.setter will be called whenever you try to set the value of x. The underlying "private" attribute _x is used to store the value for x used by both the getter and setter.
class foo:
#property
def x(self):
return self._x
#x.setter
def x(self, value):
self._x = value
print("worked")
def __init__(self, x):
self._x = x
The decorator syntax can be skipped if you want more explicit names for the getter and setter methods:
class foo(object):
def __init__(self, x):
self._x = x
def _get_x(self):
return self._x
def _set_x(self, value):
self._x = value
print("worked")
x = property(_get_x, _set_x)
For an object to handle setting of attributes on itself, use the __setattr__ method. It's rather tricky to debug, so don't do this unless you know what you're doing. Good explanation
source
class Beer(object):
def __init__(self, adj):
self.adj = adj
def __setattr__(self, key, value):
print '\tSET',key,value
object.__setattr__(self, key, value) # new style (don't use __dict__)
b = Beer('tasty')
print 'BEFORE',b.adj
b.adj = 'warm'
print 'AFTER',b.adj
print b.__dict__
output
SET adj tasty
BEFORE tasty
SET adj warm
AFTER warm
{'adj': 'warm'}
In C-ish languages, I'd mask data storage details with getter/setter methods/functions like:
int getFoo();
void setFoo(int value);
I have some Python that does:
class MyClass:
def Foo(self):
...magic to access foo...
return value
What's the right way to write/name a setter for Foo? I'm sure it's more idiom than language feature but I'm not sure what's common. Maybe I need to rename Foo() to getFoo() and match it with setFoo(). I guess that's OK if that's what is usually done.
You can use a property. This is pulled directly from the docs:
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
Now you can do...
c = C()
c.x = "a"
print c.x
>>> "a"
del c.x
Keep in mind that in Python versions prior to Python 3 (e.g., Python 2.7) you need to make sure that your object is a new-style class (it must derive from object) for it to support properties like this. Granted, you probably should be using new-style classes for all your classes anyway...
Use the builtin property function:
class MyClass:
def __init__(self):
self._value = 'Initialize self._value with some value or None'
def get_foo(self):
...magic to access foo...
return self._value
def set_foo(self, value):
... magic processing for value ...
self._value = value
foo = property(get_foo, set_foo)
Now you can use access it like this:
inst = MyClass()
inst.foo = 'Some value'
print inst.foo
It will print:
'Some value'
Generally you don't need to use getters and setters in Python.
If however, you want to expose the result of a procedure as a property, you can use the #property decorator:
class MyClass:
#property
def foo(self):
# operation
return value
#foo.setter
def foo(self, value):
# operation storing value
It is more common to just store the value of foo in an attribute. This can be calculated in the __init__ instance initializer:
class MyClass:
def __init__(self):
self.foo = someCalculationDeterminingFoo()