Python extremely dynamic class properties - python

To create a property in a class you simply do self.property = value. I want to be able to have the properties in this class completely dependent on a parameter. Let us call this class Foo.
instances of the Foo class would take a list of tuples:
l = [("first","foo"),("second","bar"),("anything","you get the point")]
bar = Foo(l)
now the instance of the Foo class we assigned to bar would have the following properties:
bar.first
#foo
bar.second
#bar
bar.anything
#you get the point
Is this even remotely possible? How?

I thought of another answer you could use using type(). It's completely different to my current answer so I've added a different answer:
>>> bar = type('Foo', (), dict(l))()
>>> bar.first
'foo'
>>> bar.second
'bar'
>>> bar.anything
'you get the point'
type() returns a class, not an instance, hence the extra () at the end.

These are called attributes, rather than properties. With that in mind, the method setattr() becomes more obvious:
class Foo(object):
def __init__(self, l):
for k, v in l:
setattr(self, k, v)
This takes each key-value pair in l and sets the attribute k on the new instance of Foo (self) to v.
Using your example:
l = [("first","foo"),("second","bar"),("anything","you get the point")]
bar = Foo(l)
print bar.first
#foo
print bar.second
#bar
print bar.anything
#you get the point

There are two ways to do this:
Use setattr like this. This approach is feasible if you only need to process the initial list once, when the object is constructed.
class Foo:
def __init__(self, l):
for (a, b) in l:
setattr(self, a, b)
Define a custom __getattr__ method. Preferably, you would store the properties in a dict for faster lookup, but you can also search the original list. This is better if you want to later modify the list and want this to be reflected in the attributes of the object.
class Foo:
def __init__(self, l):
self.l = l
def __getattr__(self, name):
for a in self.l:
if a[0] == name:
return a[1]
return None

Something like this?
>>> class Foo:
... def __init__(self, mylist):
... for k, v in mylist:
... setattr(self, k, v)
...
>>> l = [("first","foo"),("second","bar"),("anything","you get the point")]
>>> bar = Foo(l)
>>> bar.first
'foo'
>>> bar.second
'bar'
>>> bar.anything
'you get the point'
Using setattr you can do this by passing in the list and just iterating through it.

setattr works.
>>> class Foo:
... def __init__(self,yahoo):
... for k,v in yahoo:
... setattr(self,k,v)
...
>>> l = [("first","foo"),("second","bar"),("anything","you get the point")]
>>> bar = Foo(l)
>>> print bar.first
foo
>>> print bar.second
bar
>>> print bar.anything
you get the point

Related

Updating a dictionary privately?

I have a class object that receives some data. Based on a condition, I need that data to change, but only under that condition. Problem I'm running into is that when I call dict.update() , it updates the original variable too. So a subsequent request comes in, and now that original variable is "tainted" so to speak, and is using overridden information that it shouldn't have.
Assuming a dictionary like this:
my_attributes = {"test": True}
And some logic like this:
class MyClass(object):
def __init__(self, attributes):
if my_condition():
attributes.update({"test": False})
The end result:
>>> my_attributes
{'test': False}
So, the next time MyClass is used, those root attributes are still overridden.
I've seemingly gotten around this problem by re-defining attributes:
class MyClass(object):
def __init__(self, attributes):
if my_condition():
attributes = {}
attributes.update(my_attributes)
attributes.update({"test": False})
This has seemed to get around the problem, but I'm not entirely sure this is a good, or even the right, solution to the issue.
Something like this:
class MyClass(object):
#staticmethod
def my_condition():
return True
def __init__(self, attributes):
self.attributes = {**attributes}
if MyClass.my_condition():
self.attributes["test"] = False
my_attributes = {"test": True}
cls_obj = MyClass(my_attributes)
print("my_attributes:", my_attributes, "class.attributes:", cls_obj.attributes)
Output:
my_attributes: {'test': True} class.attributes: {'test': False}
You pass a (mutable) dictionary reference to an object. Now, you have two owners of the reference: the caller of the constructor (the "external world" for the object) and the object itself. These two owners may modify the dictionary. Here is an illustration:
>>> d = {}
>>> def ctor(d): return [d] # just build a list with one element
>>> L = ctor(d)
>>> d[1] = 2
>>> L
[{1: 2}]
>>> L[0][3] = 4
>>> d
{1: 2, 3: 4}
How do you prevent this? Both owners want to protect themselves from wild mutation of their variables. If I were the external world, I would like to pass an immutable reference to the dict, but Python does not provide immutable references for dicts. A copy is the way to go:
>>> d = {}
>>> L = ctor(dict(d)) # I don't give you *my* d
>>> d[1] = 2
>>> L
[{}]
If I were the object, I would do a copy of the object before using it:
>>> d = {}
>>> def ctor2(d): return [dict(d)] # to be sure L[0] is *mine*!
>>> L = ctor2(dict(d)) # I don't give you *my* d
But now you have made two copies of the object just because everyone is scared to see its variables modified by the other. And the issue is still here if the dictionary contains (mutable) references.
The solution is to spell out the responsibilities of each one:
class MyClass(object):
"""Usage: MyClass(attributes).do_something() where attributes is a mapping.
The mapping won't be modified"""
...
Note that this is the common expected behavior: unless specified, the arguments of a function/contructor are not modified. We avoid side effect when possible, but that's not always the case: see list.sort() vs sorted(...).
Hence I think your solution is good. But I prefer to avoid too much logic in the constructor:
class MyClass(object):
#staticmethod
def create_prod(attributes):
attributes = dict(attributes)
attributes.update({"test": False})
return MyClass(attributes)
#staticmethod
def create_test(attributes):
return MyClass(attributes)
def __init__(self, attributes):
self._attributes = attributes # MyClass won't modify attributes

Set an optional variable in named tuple

from collections import namedtuple
FooT = namedtuple('Foo', 'foo bar')
def Foo(foo=None, bar=None):
return FooT(foo,bar)
foo = Foo()
foo.foo = 29
throws attribute error
So, my use case is a datastructure which have optional fields.. but should be able to modify it if desired..
A defaultdict should be appropriate for what you want. It works by providing it a function on construction which it calls every time an unset element is accessed. Here's a demo:
>>> from collections import defaultdict
>>> d = defaultdict(lambda:None)
>>> d['foo'] = 10
>>> d['bar'] = 5
>>> print d['baz']
None
>>> d['baz'] = 15
>>> print d['baz']
15
Tuples are, by definition, immutable. Namedtuples follow this pattern as well.
In python3 it appears there is a SimpleNamespace [1] that you can use. If you want to simply use a read/write datastructure though you could create a class and put constraints on its members.
[1] - Why Python does not support record type i.e. mutable namedtuple
A namedtuple, like a tuple is not modifiable. Since the question is about namedtuple, in some case you may find ok (or sometimes even preferable) to create a new object with the _replace method. Of course the other references to the same object will be unchanged.
from collections import namedtuple
FooT = namedtuple('Foo', 'foo bar')
def Foo(foo=None, bar=None):
return FooT(foo,bar)
foo = Foo()
foo = foo._replace(foo=29)
For a slight variation on the above answers, why not extend the advice in the tutorial and have a class that returns None for any undefined attribute? For example:
class Foo(object):
def __getattr__(self, name):
return None
This is much the same as a defaultdict, but accessible via direct attributes much like a named tuple.
Why not just use a class?
class Foo(object):
def __init__(foo=None, bar=None):
self.foo = foo
self.bar = bar
foo = Foo()
foo.foo = 29

modify setattr to mass-assign multiple attributes to different values

I was trying to make a class that can assign multiple attributes to different values at one go. So, I tried to modify the setattr to do this.
class hello():
def __setattr__(self,attr,value):
if type(attr) == str:
self.__dict__[attr] = value
elif type(attr) == list:
for item,val in zip(attr,value):
self.__dict__[item] = val
else:
print "Error!!!"
so that my desired class would have the property:
>>> hola = hello()
>>> setattr(hola,["a","b"],[1,2])
>>> hola.a
1
>>> hola.b
2
But doing this returns
TypeError: attribute name must be string, not 'list'
It is true that I can define a special method inside the class for this job, like
def assigner(self, attrlist, valuelist):
for item,value in zip(attrlist,valuelist):
self.__dict__[item] = value
But wanted to know why it is not possible via setattr and most efficient way to do this job
From the docs on the setattr function:
The arguments are an object, a string and an arbitrary value.
The second argument has to be str.
The possible workaround is to define __setitem__ instead of __setattr__
class hello():
def __setitem__(self, k, v):
self.__dict__.update(zip(k, v) if type(k) is tuple else [(k, v)])
This will allow you to assign values in dict style
In [2]: heya = hello()
In [3]: heya['a', 'b'] = [1, 2]
In [4]: heya.a
Out[4]: 1
In [5]: heya.b
Out[5]: 2
Still, nothing stops you from assigning them like this
In [15]: heya.a, heya.b = [1, 2]
Well the TypeError states it pretty well: the name (second argument) must be a string, not a list.
The arguments are an object, a string and an arbitrary value
https://docs.python.org/2/library/functions.html#setattr

What does a class need to implement in order to be used as an argument tuple?

I want to be able to do something like the following
class C(object):
# I store a series of values in some way
# what do I need to implement to act like an array of arguments
c=C()
result=f(*c)
What does the *"operator" invoke on the instance in this usage?
There are two ways to control the behavior of the * operator when it is used like that:
Overload the __iter__ special method:
>>> class C(object):
... def __init__(self, lst):
... self.lst = lst
... def __iter__(self):
... return iter(self.lst)
...
>>> def f(a, b, c):
... print "Arguments: ", a, b, c
...
>>> c = C([1, 2, 3])
>>> f(*c)
Arguments: 1 2 3
>>>
Overload the __getitem__ special method:
>>> class C(object):
... def __init__(self, lst):
... self.lst = lst
... def __getitem__(self, key):
... return self.lst[key]
...
>>> def f(a, b, c):
... print "Arguments: ", a, b, c
...
>>> c = C([1, 2, 3])
>>> f(*c)
Arguments: 1 2 3
>>>
One way you can do it is to subclass tuple or list.
People call this "positional expansion" or argument unpacking. Your instance should provide an __iter__ method, which is called when iterating over this object. However, I think the cleanest approach would be to subclass collections.Iterable, which is the abstract base class for all iterables in Python.
Note that this is the same question for keyword argument unpacking, requiring the object to be a mapping.
Edit: I am still trying to find the exact implementation in this case to see which C API call is used for unpacking. This would yield the precise answer to this question. Any pointers?

Making an object's attributes iterable

I'm getting returned a list with objects that have multiple attributes like so:
results = q.fetch(5)
for p in results:
print "%s %s, %d inches tall" % (p.first_name, p.last_name, p.height
Is it possible to iterate over these attributes so I can do something like for x in p. I want to check the value of each one, but I don't want to create a huge block of IF statements.
I warn against doing this. There are rare exceptions where it's warranted, but almost all the time it's better avoiding this sort of hackish solution. If you want to though, you could use vars() to get a dictionary of attributes and iterate through it. As #Nick points out below, App Engine uses properties instead of values to define its members so you have to use getattr() to get their values.
results = q.fetch(5)
for p in results:
for attribute in vars(p).keys()
print '%s = %s' % (attribute, str(getattr(p, attribute)))
Demonstration of what vars() does:
>>> class A:
... def __init__(self, a, b):
... self.a = a
... self.b = b
...
>>> a = A(1, 2)
>>> vars(a)
{'a': 1, 'b': 2}
>>> for attribute in vars(a).keys():
... print '%s = %s' % (attribute, str(getattr(a, attribute)))
...
a = 1
b = 2
You can subclass the original variable type, and define your own cunning iter(self) function, to get what you want.
e.g. to change the way a dictionary iterates:-
>>> class mydict(dict):
... def __iter__(self):
... for i in self.items():
... yield i
...
>>> x = mydict( {'a' : 1, 'b':2 } )
>>> for i in x:
... print i
...
('a', 1)
('b', 2)
To get a list of properties on a model class, call Model.properties() (or instance.properties() - it's a class method). This returns a dictionary mapping property names to Property class instances; you can fetch the value of the properties by doing getattr(instance, name).
If you're using Expando, there's also instance.dynamic_properties(), which returns a list of dynamically defined properties on that object.
With the assumption that the object you get back from q.fetch(5) having a __dict__ attribute, you can simply use pprint to display your information.
>>> import pprint
>>> results = q.fetch(5)
>>> pprint.pprint(results.__dict__())
Or alternatively, if it has something that can be converted to a dictionary, a similar notation would work
>>> pprint.pprint(dict(results.dict_like_property))
I would suggest though, that this isn't a good approach to take, but it does hold for debugging code easily.

Categories

Resources