Class attributes with a "calculated" name - python

When defining class attributes through "calculated" names, as in:
class C(object):
for name in (....):
exec("%s = ..." % (name,...))
is there a different way of handling the numerous attribute definitions than by using an exec? getattr(C, name) does not work because C is not defined, during class construction...

How about:
class C(object):
blah blah
for name in (...):
setattr(C, name, "....")
That is, do the attribute setting after the definition.

class C (object):
pass
c = C()
c.__dict__['foo'] = 42
c.foo # returns 42

If your entire class is "calculated", then may I suggest the type callable. This is especially useful if your original container was a dict:
d = dict(('member-%d' % k, k*100) for k in range(10))
C = type('C', (), d)
This would give you the same results as
class C(object):
member-0 = 0
member-1 = 100
...
If your needs are really complex, consider metaclasses. (In fact, type is a metaclass =)

What about using metaclasses for this purpose?
Check out Question 100003 : What is a metaclass in Python?.

Related

Changing an object's class while maintaining its attributes and functions

If I have 2 classes defined like this:
class A(object):
a = 10
class B(A):
b = 20
If I create an object:
c = A()
And then do:
c.__class__ = B
Is it a valid way to change ('upgrading') the class of the object, maintaining the primary class attributes and methods and gaining the secondary class attributes and methods?
If true, this only makes sense for this cases where the class to which we are changing the object inherits from the previous class? Best regards.
UPDATED:
To give more context.
I have the following class EmbrionDevice.
class EmbrionDevice(object):
def __init__(self, device_info, *args, **kwargs):
super(EmbrionDevice, self).__init__(*args, **kwargs)
# Serial number unique 64-bit address factory-set
self.shl = device_info['source_addr_long']
# 16-bit network address
self.my = device_info['source_addr']
# Node identifier
self.ni = device_info['node_identifier']
# Parent Address
self.pa = device_info['parent_address']
# Device type, 0-coordinator, 1-router, 2-End Device
self.dt = device_info['device_type']
# Device type identifier xbee or Digi device
self.dd = device_info['device_type_identifier']
# Device attributes summary in a dictionary
self.info = device_info
# Embrion future function
self.function_identifier = None
# Device state definition
self.state = DEV_STATE_CODES['embrion']
self.status = DEV_STATUS_CODES['no status']
That i would later like to change/upgrade, to one of the following specific device classes:
class PassiveDevice(EmbrionDevice):
pass
class ActiveDevice(EmbrionDevice):
pass
Basically i wanted to ease my copy, avoiding the copy of all the attributes.
This is not a valid way to change class of a instance object, A simple example can demonstrate it :-
class A(object):
a = 10
def __init__(self):
self.b = 20
self.c = 30
class B(A):
d = 35
def __init__(self):
self.x = 70
self.y = 80
c = A()
c.__class__ = B
print c
<__main__.B object at 0x02643F10>
So now c is instance of class B, Try printing instance attributes:
print c.x
print c.y
It says:
AttributeError: 'B' object has no attribute 'x'
That's definitely a hack, and this is also a hack, but I find it do be a bit cleaner:
In [1]: class A(object):
...: a = 10
...:
In [2]: class B(A):
...: b = 20
...:
In [3]: c = A()
In [4]: new_c = B()
In [5]: new_c.__dict__.update(c.__dict__.copy())
In [7]: repr(new_c)
Out[7]: '<__main__.B object at 0x102f32050>'
In [8]: new_c.b
Out[8]: 20
I'm not sure if your approach would work or not, but, this way, you're copying the properties of the old object into a new object that was properly instantiated. If you change .__class__, you can't guarantee that the old variable will reference a properly-created new-class object, as __init__(), __new__(), etc. wouldn't run.
To copy functions, and, this is ugly... but, you could take an approach like this:
In [18]: for name, obj in c.__class__.__dict__.iteritems():
....: if hasattr(obj, '__call__'):
....: # Copy the function.
....:
test
There are various hacky methods of adding functions to an existing object dynamically. They're all ugly, but, they can be found here.
You have a misunderstanding of what are "class attributes" in Python -
All instance attributes are kept in the instance itself: it does have a __dict__ attribute which is a dictionary where all the attributes defined by code like self.shl = device_info['source_addr_long'] is kept. (This statement creates an shl entry on that dict, for example).
These assignments are run inside the __init__method. If you change an object's class by assigning to its __class__ , it works in a sense: that is its new class. The methods the new class may have defined are now acessible. But all the attributes which were set in the previous class' __init__ still exist, because they are set on the instance's __dict__ that was not changed;. From what I got, this may be exactly what you want - but please note that methods on the original class (as well as class attributes - i.e., attributes defined on the class body itself) will not be acessible, unless the new class itself inherits from the original class. As in the example you show, this is what you are doing, this approach might actually work for you.
But be careful, and do some extensive unit testing, and testing on the interactive console.
An alternative for you might be to use zope.interface - tis will allow you to have a single object, but that "looks like" an object with different attributes and methods to other parts of the code, which might need an specific interface.

Class (not instance) constructor

Basically I'm trying to do something like this:
class A:
some_field = None # that is the field which should have 123 value
# what needs to be here?
class B(A(123)):
pass
class C(A(456)):
pass
I want one base class A. And I want children B and C to be able to pass arguments to it like that A(123).
And for example if I do B.some_field I should get 123
How can I do that?
unutbu's answer is correct. I'd just like to point out that you can even do this stuff dynamically...
def make_class(base, value):
class NewClass(base):
some_field = value
return NewClass
class A(object):
some_field = None
B = make_class(A, 123)
C = make_class(A, 456)
And actually, this one is important enough that python actually has a builtin to do it:
class A(object):
some_field = None
B = type('B', (A,), {'some_field': 123})
type takes 3 arguments -- The name of the class, a tuple of the base classes and a dictionary of class attributes.
If you define B and C like this:
class B(A):
some_field = 123
class C(A):
some_field = 123
then:
In [147]: B.some_field
Out[147]: 123
By the way, A(123) would pass 123 to A.__init__. It would not set some_field.
Also, in a class definition,
class B(...)
The stuff in parentheses must be a class or a comma-separated list of classes (or, in Python3, metaclass=SomeMetaClass). It can not be an instance of a class. So
class B(A(123))
is a definite no-no.
In response to the comment: You could use the A.__new__ method to return a class:
class A(object):
some_field = None
def __new__(cls, val):
cls.some_field = val
return cls
class B(A(123)): pass
then
In [161]: B.some_field
Out[161]: 123
But this is a non-standard use of __new__ since usually it is used to return an instance of A. When it does not return an instance of A, then A.__init__ is not called.
Instead of using a class A it would be more understandable to use a class factory:
class B(make_class(123)): ...
as you mentioned in the comment below.

nested classes in Python

Dealing with classes (nested etc) does not look easy in Python, surprisingly! The following problem appeared to me recently and took several hours (try, search ...) without success. I read most of SO related links but none of them has pointed the issue presented here!
#------------------------------------
class A:
def __init__(self):
self.a = 'a'
print self.a
class B(A):
def __init__(self):
self.b = 'b'
A.a = 'a_b'
print self.b, A.a
#------------------------------------
class C:
class A:
def __init__(self):
self.a = 'a'
print self.a
class B(A):
def __init__(self):
self.b = 'b'
A.a = 'a_b'
print self.b, A.a
#------------------------------------
#------------------------------------
>>> c1 = A()
a
>>> c1.a
'a'
>>> c2 = B()
b
>>> c2.a, c2.b
('a_b', 'b')
>>> c3 = C()
>>> c4 = c3.A()
a
>>> c4.a
'a'
>>> c5 = c3.B()
b a_b
>>> c5.b
'b'
>>> c5.a
Traceback (most recent call last):
File "", line 1, in
AttributeError: B instance has no attribute 'a'
Where is the problem in the code?
AND
In both cases it seems that when B(A) is initialized A() is not initialized. What is the solution for this issue? Note that the term A.__init__() being called inside B()'s __init__() does not work!
Updates:
class Geometry:
class Curve:
def __init__(self,c=1):
self.c = c #curvature parameter
print 'Curvature %g'%self.c
pass #some codes
class Line(Curve):
def __init__(self):
Geometry.Curve.__init__(self,0) #the key point
pass #some codes
g = Geometry()
C = g.Curve(0.5)
L = g.Line()
which results in:
Curvature 0.5
Curvature 0
what I was looking for.
The code executed in a method runs in the local scope of that method. If you access an object that is not in this scope, Python will look it up in the global/module scope, NOT in the class scope or the scope of any enclosing class!
This means that:
A.a = 'a_b'
inside C.B.__init__ will set the class attribute of the global A class, not C.A as you probably intended. For that you would have to do this:
C.A.a = 'a_b'
Also, Python will not call parent methods if you override them in subclasses. You have to do it yourself.
The scoping rules mean that if you wanted to call the __init__ method of the parent class inside C.B.__init__, it has to look like this:
C.A.__init__(self)
and NOT like this:
A.__init__(self)
which is probably what you've tried.
Nested classes seems so unpythonic, even if considered as factories. But to answer your question: There simply is no c5.a (instance of C.B). In the init-method of C.B you add to the CLASS C.A an attribute a, but not to C.B! The class A does already have an attribute a, if instantiated! But the object of class B (and even the class) doesn't!
You must also keep in mind, that __init__ is not an constructor like in C++ or Java! The "real constructor" in python would be __new__. __init__ just initializes the instance of a class!
class A:
c = 'class-attribute'
def __init__(self):
self.i = 'instance-attribute'
So in this example c is a class-attribute, where i is an attribute of the instance.
Even more curios, is your attempt to add an attribute to the baseclass at the moment of the instantiation of the child-class. You are not getting a "late" inheritance-attribute that way.
You simply add to the class A an additional attribute, which surprises me to even work. I guess you are using python 3.x?
The reason for this behaviour? Well, i guess it has to do with pythons neat feature that in python definitions are executed(AFAIK).
The same reason why:
def method(lst = []):
is almost ever a bad idea. the deafult-parameter gets bound at the moment of the definition and you won't generate a new list-object every-time you call the method, but reusing the same list-object.

Access to class attributes of parent classes

I would like to know what is the best way to get all the attributes of a class when I don't know the name of them.
Let's say I have:
#!/usr/bin/python2.4
class A(object):
outerA = "foobar outerA"
def __init__(self):
self.innerA = "foobar innerA"
class B(A):
outerB = "foobar outerB"
def __init__(self):
super(B, self).__init__()
self.innerB = "foobar innerB"
if __name__ == '__main__':
b = B()
Using hasattr/getattr to get the "outerA" class field of my b instance works fine:
>>> print hasattr(b, "outerA")
>>> True
>>> print str(getattr(b, "outerA"))
>>> foobar outerA
Ok, that's good.
But what if I don't exactly know that b has inherited a field called "outerA" but I still want to get it?
To access b's inner fields, I usually use b.__dict__ . To get b.outerB, I can use b.__class__.__dict__ but that still doesn't show "outerA" among it fields:
>>> print "-------"
>>> for key, val in b.__class__.__dict__.iteritems():
... print str(key) + " : " + str(val)
>>> print "-------\n"
Shows:
-------
__module__ : __main__
__doc__ : None
__init__ : <function __init__ at 0xb752aaac>
outerB : foobar outerB
-------
Where's my outerA?? :D
I am sure I can keep "climbing" the class hierarchy and get all the fields, but that doesn't seem like the greatest solution...
As there are hasattr(), getattr()... isn't there something like a listattr() that would give all the available attributes of an instance?
Thank you!
I think you're looking for dir() - it works just as well within code as in the shell.
Try dir(b). The resulting list includes inherited attributes.

Using Variables for Class Names in Python?

I want to know how to use variables for objects and function names in Python. In PHP, you can do this:
$className = "MyClass";
$newObject = new $className();
How do you do this sort of thing in Python? Or, am I totally not appreciating some fundamental difference with Python, and if so, what is it?
Assuming that some_module has a class named "class_name":
import some_module
klass = getattr(some_module, "class_name")
some_object = klass()
I should note that you should be careful here: turning strings into code can be dangerous if the string came from the user, so you should keep security in mind in this situation. :)
One other method (assuming that we still are using "class_name"):
class_lookup = { 'class_name' : class_name }
some_object = class_lookup['class_name']() #call the object once we've pulled it out of the dict
The latter method is probably the most secure way of doing this, so it's probably what you should use if at all possible.
In Python,
className = MyClass
newObject = className()
The first line makes the variable className refer to the same thing as MyClass. Then the next line calls the MyClass constructor through the className variable.
As a concrete example:
>>> className = list
>>> newObject = className()
>>> newObject
[]
(In Python, list is the constructor for the list class.)
The difference is that in PHP, you represent the name of the class you want to refer to as a string, while in Python you can reference the same class directly. If you must use a string (for example if the name of the class is created dynamically), then you will need to use other techniques.
If you need to create a dynamic class in Python (i.e. one whose name is a variable) you can use type() which takes 3 params:
name, bases, attrs
>>> class_name = 'MyClass'
>>> klass = type(class_name, (object,), {'msg': 'foobarbaz'})
<class '__main__.MyClass'>
>>> inst = klass()
>>> inst.msg
foobarbaz
Note however, that this does not 'instantiate' the object (i.e. does not call constructors etc. It creates a new(!) class with the same name.
If you have this:
class MyClass:
def __init__(self):
print "MyClass"
Then you usually do this:
>>> x = MyClass()
MyClass
But you could also do this, which is what I think you're asking:
>>> a = "MyClass"
>>> y = eval(a)()
MyClass
But, be very careful about where you get the string that you use "eval()" on -- if it's come from the user, you're essentially creating an enormous security hole.
Update: Using type() as shown in coleifer's answer is far superior to this solution.
I use:
newObject = globals()[className]()
I prefer using dictionary to store the class to string mapping.
>>> class AB:
... def __init__(self, tt):
... print(tt, "from class AB")
...
>>> class BC:
... def __init__(self, tt):
... print(tt, "from class BC")
...
>>> x = { "ab": AB, "bc": BC}
>>> x
{'ab': <class '__main__.AB'>, 'bc': <class '__main__.BC'>}
>>>
>>> x['ab']('hello')
hello from class AB
<__main__.AB object at 0x10dd14b20>
>>> x['bc']('hello')
hello from class BC
<__main__.BC object at 0x10eb33dc0>

Categories