Let's say I create an instance of a class and want to assign some values to its public properties. Usually, this would be done like this:
class MyClass:
def __init__(self):
self.name = None
self.text = None
myclass = MyClass()
myclass.name = 'My name'
But, what if a write a function that takes a class as parameter and I would like to assign some values to the public properties of that class dynamically - that is via variables and loops (without knowing how many there are or what they are called.)
The obvious would be:
myclass = MyClass()
myclass['name'] = "My name"
But that doesn't work.
Any ideas?
setattr(my_class_instance, 'attr_name', attr_value)
After reading rejected Syntax For Dynamic Attribute Access I'm using a mixin class providing dictionary-style access to an object's attributes :
class MyClass:
def __init__(self):
self.name = None
self.text = None
def __getitem__(self, name):
return getattr(self, name)
def __setitem__(self, name, value):
return setattr(self, name, value)
def __delitem__(self, name):
return delattr(self, name)
def __contains__(self, name):
return hasattr(self, name)
While still being able to set attributes directly:
myclass = MyClass()
myclass.name = "foo"
myclass.text = "bar"
it's then possible to set them dynamically :
for attr in ('name', 'text'):
myclass[attr] = confirm(attr, default=myclass[attr])
Using dir with setattr should do the job
class MyClass:
def __init__(self):
self.name = None
self.text = None
myclass = MyClass()
myclass.name = 'My name'
for prop in dir(myclass):
print '%s:%s'%(prop,getattr(myclass,prop))
print
for prop in dir(myclass):
if prop[:2]!='__' and prop[-2:]!='__':
print prop[-2:]
setattr(myclass,prop,"Foo Bar")
for prop in dir(myclass):
print '%s:%s'%(prop,getattr(myclass,prop))
But be careful because this code also sets '__doc__', '__init__', '__module__' properties to "Foo Bar". So you will have to take care of avoiding certain things given to you by dir (especially those which start and end with __ double underscores).
I don't know if this solution is a good or bad idea.
But this works for me.
myclass = MyClass()
myclass.__dict__['name'] = "My name"
Related
I have a class as follows:
class A:
def __init__(self):
pass
def add_attr(self, name):
setattr(self, name, 'something')
How do I define custom setter, getter for self.name? I cannot use __setattr__, __getattribute__ because that will change the behaviour of add_attr too.
EDIT: the users of this class will add arbitrary number of attributes with arbitrary names:
a = A()
a.add_attr('attr1')
a.add_attr('attr2')
I want custom behavior for only these user added attributes.
Building off #Devesh Kumar Singh’s answer, I would implement it in some way like this:
class A:
def __init__(self):
self.attrs = {}
def __setattr__(self, key, value):
if key in self.attrs:
self.set_attr(key, value)
else:
object.__setattr__(self, key, value)
def __getattribute__(self, key):
if key in self.__dict__.get(attrs, {}):
return self.__dict__['get_attr'](self, key)
return object.__getattribute__(self, key)
def get_attr(self, key):
r = self.attrs[key]
# logic
return r
def set_attr(self, key, value):
# logic
self.attrs[key] = value
def add_attr(self, key, value=None):
self.attrs[key] = value
add_attr is only used to initialise the variable the first time. You could also edit __setattr__ to set all new attributes in the self.attrs rather than self.__dict__
Custom getter and setter logic? That's what a property is made for. Usually these are used to magically mask function calls and make them look like attribute access
class MyDoubler(object):
def __init__(self, x):
self._x = x
#property
def x(self):
return x * 2
#x.setter
def x(self, value):
self._x = value
>>> md = MyDoubler(10)
>>> md.x
20
>>> md.x = 20
>>> md.x
40
>>> md._x
20
But there's no rule saying you can't abuse that power to add custom behavior to your getters and setters.
class A(object):
def __init__(self):
pass
#staticmethod
def default_getter_factory(name):
def default_getter(self):
return self.name
return default_getter
#staticmethod
def default_setter_factory(name):
def default_setter(self, value):
setattr(self, name, value)
return default_setter
def add_attr(self, name, getterfactory=None, setterfactory=None):
private_name = f"_{name}"
if getterfactory is None:
getterfactory = self.__class__.default_getter_factory
if setterfactory is None:
setterfactory = self.__class__.default_setter_factory
getter, setter = getterfactory(private_name), setterfactory(private_name)
getter = property(getter)
setattr(self.__class__, name, getter)
setattr(self.__class__, name, getter.setter(setter))
That said this is all a bit silly, and chances are that whatever it is you're trying to do is a thing that shouldn't be done. Dynamic programming is all well and good, but if I were to review code that did this, I would think very long and hard about alternative solutions before approving it. This reeks of technical debt to me.
One possibility I could think of is to have a dictionary of dynamic attributes, and set and get the dynamic attributes using the dictionary
class A:
def __init__(self):
#Dictionary of attributes
self.attrs = {}
#Set attribute
def set_attr(self, name):
self.attrs[name] = 'something'
#Get attribute
def get_attr(self, name):
return self.attrs.get(name)
a = A()
a.set_attr('var')
print(a.get_attr('var'))
The output will be something
Or an alternate is to use property decorator to add arguments explicitly outside the class, as described here
class A:
def __init__(self):
pass
a = A()
#Add attributes via property decorator
a.attr_1 = property(lambda self: self.attr_1)
a.attr_2 = property(lambda self: self.attr_2)
#Assign them values and print them
a.attr_1 = 4
a.attr_2 = 6
print(a.attr_1, a.attr_2)
The output will be 4 6
I am gonna answer my own question just for reference. This is based on others' answers here. The idea is to use default __setattr__ and __getattribute__ on attributes not added through add_attr.
class A:
def __init__(self):
self.attrs = {}
def add_attr(self, name):
self.attrs[name] = 'something'
def __getattribute__(self, name):
try:
object.__getattribute__(self, 'attrs')[name] # valid only if added by user
# custom logic and return
except (KeyError, AttributeError):
return object.__getattribute__(self, name)
def __setattr__(self, name, val):
# similar to __getattribute__
I would like to dynamically bind descriptors to attribute of a class.
For example I have this descriptor (it is just a dummy example):
class Item(object):
def __init__(self, filename):
self.filename = filename
def __get__(self, obj=None, objtype=None):
#print '__get__(%s, %s)' % (obj, objtype)
return open(self.filename).read()
def __set__(self, obj, val):
#print '__set__(%s, %s)' % (obj, val)
open(self.filename, 'w').write(str(val))
In my main container, I would like to dynamically register my descriptors.
Everything works great if I instanciate the descriptors at the class level:
class Container(object):
foo = Item('foo')
bar = Item('bar')
Unfortunately when I try to associate the descriptor dynamically using setattr I need to put a lot more complexity to my class:
class Container(object):
def __init__(self, data):
for attr in data:
super(Container, self).__setattr__(attr, Item(attr))
def __setattr__(self, name, value):
#print '__setattr__(%s, %s)' % (name, value)
attr = super(Container, self).__getattribute__(name)
if hasattr(attr, '__set__'):
attr.__set__(name, value)
else:
super(Container, self).__setattr__(name, value)
def __getattribute__(self, name):
#print '__getattribute__(%s)' % (name)
attr = super(Container, self).__getattribute__(name)
if hasattr(attr, '__get__'):
return attr.__get__(name)
return attr
The expected output is:
>>> c = Container(['foo', 'bar'])
>>> c.foo = 2
>>> c.foo
'2'
Is there a simpler solution with less kludges?
So, you're almost there with your __init__ in container. The problems you have:
in 99.9% cases you sould never call magic (dunder, __) functions directly. So, your super(...).__setattr__ makes no sense, tbh. There's setattr for this
Tricky part with descriptors (btw, from my experience, it's kind of "default" obstruction when people start using them). When you use descriptors in non-dynamic way with
class Container(object):
foo = Item('foo')
bar = Item('bar')
you are setting foo and bar in scope of the class - literally as class attributes. But in your "dynamic" way you're doing it with instance. Idk if you tried to set it as class, but if this was an intention, super doesn't work like this. To set attach descriptor dynamically, you need to attach it to class of your instance (referred by self inside __init__). To do so, access self.__class__ or type(self). So, your code may look like
class Container(object):
def __init__(self, data):
for attr in data:
setattr(type(self), attr, Item(attr))
I would like to know if there is an easy way to do some identical edits on several methods of a class. An example :
class Dog():
def __init__(self):
self.name = 'abc'
self.age = 1
def setName(self, newValue):
self.name = newValue
def setAge(self, newValue):
self.age = newValue
class TalkingDog(Dog):
def __init__(self):
super().__init__()
# The end is in pseudo code :
for method in TalkingDog.allMethods :
method = method + 'print('I have been edited !')'
I know that I can also overwrite each method but in a situation with tens of methods, that will be a little boring...
So I tried this :
class TalkingDog(Dog):
def __init__(self):
super().__init__()
for method in self.__dir__():
if method.startswith('set'):
oldMethod = getattr(self, method)
def _newMethod(newValue):
oldMethod(newValue)
print('I have been edited !')
setattr(self, method, _newMethod)
a = TalkingDog()
print(a.setName) >>> <function TalkingDog.__init__.<locals>._newMethod at 0x0000000002C350D0>
That almost works but setName is not anymore a method. It's an attribute which contains a function. I completely understand why but I'm trying to get a cleaner result. With that result, I risk of having problems later. For example I can't use the library pickle with that object (got the error _pickle.PicklingError: Can't pickle <function TalkingDog.__init__.<locals>._newMethod at 0x00000000003DCBF8>: attribute lookup _newMethod on __main__ failed).
The Pythonic way to do this is probably to use the descriptor protocol, which is also what properties use:
class VocalAttribute:
def __init__(self, name, feedback):
"""Called when you first create the descriptor."""
self.name = name # the name of the attribute 'behind' the property
self.feedback = feedback # the feedback to show when the value changes
def __get__(self, obj):
"""Called when you get the descriptor value."""
return getattr(obj, self.name)
def __set__(self, obj, value):
"""Called when you set the descriptor value."""
prev = getattr(obj, self.name, None)
if value != prev:
setattr(obj, self.name, value)
print(self.feedback)
def __delete__(self, obj):
"""Called when you delete the descriptor value."""
delattr(obj, self.name)
class Foo:
bar = VocalAttribute('_bar', 'I have been edited!')
foo = Foo()
print('1.')
foo.bar = 'hello'
print('2.')
foo.bar = 'hello'
print('3.')
foo.bar = 'world'
Output:
1.
I have been edited!
2.
3.
I have been edited!
Note that this only gives feedback when the new value is different to the old one - you can tweak the behaviour as needed in __set__. It also means you can directly read from and assign to foo.bar, rather than needing to call getters and setters (what is this, Java?)
since decorator could explicit called here a way to use it:
def updater(obj, call_back, call_back_args=(), call_back_kw=None, replace=False):
# ability to be called on the fly with different args and kw for the callback
# now it returns the updated obj (instance or class)
# but could a be factory returning a new obj in this case make a copy of obj, update this coy and return it
def update_function(fn, *args, **kw):
def wrapper(*args, **kw):
if replace:
# call only the callback
res = call_back(*call_back_args, **call_back_kw)
else:
res = fn(*args, **kw)
call_back(*call_back_args, **call_back_kw)
return res
return wrapper
# get all methods of the obj
# and apply update_function (a decorator) to all methods
for name, m in inspect.getmembers(
obj, predicate=lambda x: inspect.isfunction(x) or inspect.ismethod(x)):
# make the selection here
# could be made on the name for instance
if not name.startswith('_'):
new_m = update_function(m)
setattr(obj, name, new_m)
return obj
# declare a callback
def call_back(*args, **kw):
# simple callback
print("I have been edited and called with %r args and %r kw " % (args, kw))
a = Dog()
# could be called on instance or class
# apply the callback on all "public" methods
updater(
a,
call_back,
call_back_args=(2, 3, 4),
call_back_kw={"kw1": "v_1"}
)
I have a class that contains a dictionary, i use __getattr__(key) to get nicer access to the dictionary[key] now i would like to be able to set things in the dictionary with the same access style.
Class foo(object):
def __init__(self, name):
self.props = {"name":name}
def __getattr__(self, attribute):
return self.props[attribute]
This is so i can access it in this way
f = foo("test")
print f.name
I would like the ability to set the attributes aswell, however using setattr is proving problematic due to it being called before anything else fails. Is there a way to make it act like getattr?
__setattr__ is fine, but you need to protect yourself from case when __setattr__ is called before self.props is set (RuntimeError: maximum recursion depth exceeded)
class foo(object):
# List of properties which are not stored in the props dict
__slots__ = ('props', 'other_property')
def __init__(self, name):
self.props = {"name":name}
self.other_property = 2
def __getattr__(self, attribute):
return self.props[attribute]
def __setattr__(self, name, value):
if name in self.__slots__:
super(foo, self).__setattr__(name, value)
else:
self.props[name] = value
f = foo("name")
print f.name
f.value = 2
f.name = "TEST"
print f.value
print f.props
I've got a class:
class Foo():
def bar(name):
return something
and i want to have
foo = Foo()
foo.name
returning the same as foo.bar('name'). Is it possible?
This will automatically use bar if the attribute doesn't exist:
class Foo(object):
def bar(self, name):
return name
def bar2(self, attr, value):
print attr, value
def __getattr__(self, attr):
return self.bar(attr)
def __setattr__(self, attr, value):
self.bar2(attr, value)
foo = Foo()
print foo.name
foo.name = 'not name'
As you describe it now, name only exists in the scope of the function bar and thus Foo has no direct access to it unless you store it in its scope. The simplest solution would be to make a member field called name and set its value in bar.
class Foo():
def __init__(self):
self.name = None
def bar(self, name):
self.name = name
return something
Declare your class:
class Foo:
def bar(self, name):
return 'something'
You can assign the method to an attribute.
foo = Foo()
foo.name = foo.bar
If you prefer you can do it in constructor.
Your class should have an attribute to store the name.
Try this out
#!/usr/bin/python
class Foo:
def __init__(self):
self.name = ''
def bar(self, name):
self.name = name
return "name via bar(): %s" % name
f = Foo()
print f.bar("Jackson")
print f.name