Getting the class a variable resides in - python

Lets say I have something like this -
class A(object):
c = C()
class B(A):
pass
class C(object):
def __init__(self):
pass
def get_parent_class(self):
# This should return B
How would I implement get_parent_class, so that it will work as following -
B.c.get_parent_class() # Returns the class type B (not the instance b!)
Is this even possible?
I basically have a parent class (class A in our example), which contains a variable (var c in the example). I then have a child-class (class B which inherits A)
I want to use functions that C exposes on B, but in order to be used properly, I need C to know it's running on B
(Hope I didn't complicate things in the last explanation...)
Edit -
Please note that I'm not trying to get the class of C, that's an easy c.__class__. I need the class holding c
Thanks!

AFAIK, you cannot do that.
B.c just returns a reference to a C object. Same object can be a member of a list, of an instance of another class (say D) and of another class (say E)
Just add to your example :
class D:
def __init__(self, c):
self.c = c
class E:
c = C()
Then :
>>> c = B.c
>>> E.c = c
>>> d = D(c)
>>> c.a = 1
>>> B.c.a
1
>>> d.c.a
1
>>> E.c.a
1
At that point, c object itself does not know that it belongs to B, d and E. And what should c.get_parent_class() return ? How to decide between B and E ?
You could try to make C aware of its container :
class C(object):
def __init__(self, clazz):
self.clazz = clazz
def get_parent_class(self):
return self.clazz
class A(object):
c = C(A)
You would get A.c.get_parent_class() giving <class '__main__.A'> but as it would be the same object, B.c.get_parent_class() will also give<class '__main__.A'> ...

You could take a look at the __bases__ attribute of your object. It returns a tuple of base classes for an object.
You can see it in the docs here.

You can get the class from the name attribute, example:
>>> import itertools
>>> x = itertools.count(0)
>>> x.__class__.__name__
'count'
Or this way:
>>> type(x).__name__
'count'

Related

Python bind method to another instance

I need to merge two methods from different instances of different classes to a single instance of a class.
For example I define two classes:
class A:
def __init__(self):
self.name = "a"
def print_name(self):
print(self.name)
class B:
def __init__(self):
self.name = "b"
def print_name(self):
print(self.name)
and then I will try to make another object c and its print_name method must return the results of a.print_name() and b.print_name() so I tried the following :
a = A()
b = B()
c = A()
c.name = "c"
c.print_name_1 = a.print_name
c.print_name_2 = b.print_name
def final_print(self):
self.print_name_1()
self.print_name_2()
c.print_name = MethodType(final_print, c)
c.print_name()
Expected output:
c
c
but I get :
a
b
I tried to use types.MethodType as described here but it creates some kind of a method which takes two arguments : the first one will be the 'a' instance and the second one will be the 'c' instance.
Any help to do that properly?
I managed to succeed with this, thanks to this answer. I am using the __func__ attribute of a method. So here is the code :
c.name = "c"
c.print_name_1 = MethodType(lambda instance: copy.deepcopy(a.print_name).__func__(instance), c)
c.print_name_2 = MethodType(lambda instance: copy.deepcopy(b.print_name).__func__(instance), c)
def final_print(self):
self.print_name_1()
self.print_name_2()
c.print_name = MethodType(final_print, c)
c.print_name()

How to serialise both positional and non-positional arguments of Python objects?

How can I serialise test below:
class Foo:
a = 0
b = {}
def __init__(self, a, b=None):
self.a = a
if b:
self.b = b
test = Foo(1)
test.b['c'] = 2
so that the output is:
{"a": 1, "b": {"c": 2}}
I've tried:
print(json.dumps(test, default=lambda x: x.__dict__))
but it returns:
{"a": 1}
I understand that test.b['c'] = 2 does not add b or c to Foo.__dict__, which is probably why x.__dict__ in the lambda doesn't pick them up. So is the answer one of:
Do not assign key-value pairs to arbitrary objects; use setattr instead.
Do not define arbitrary classes, if its property set can evolve at runtime; use a simple dict instead.
The problem here is test.b is not a instance variable. So when you serialize the object test using json.dumps, its not finding an instance variable b at all.
If you redefine the constructor like below:
class Foo:
a = 0 #this is not instance variable - this is a class variable
b = {} #this is not instance variable - this is a class variable
def __init__(self, a, b=None):
self.a = a
self.b = {} #declared the instance variable b also
if b:
self.b = b
test = Foo(1)
test.b['c'] = 2
Now, if you run you get the desired output.

How to mark function argument to be an instance of any class, inheriting classes i set [duplicate]

This question already has an answer here:
python typing module: Mixin
(1 answer)
Closed 3 years ago.
I want to mark an argument of a function as instance of any class, inheriting classes i set.
Union is quite similar, but it works like OR, so
a: Union[A, B] # means a is an instance of A `OR` B or any inherited classes
I'd like to do the opposite:
b: Intersection[A, B] # a is an instance of any class inherited from A `AND` B
class A:
def a(self): return 0
class B:
def b(self): return 0
class C(A, B): pass
class D(C):
def b(self): return 1
class E(A, B):
def e(self): return 0
def foo(a_and_b: Intersection[A, B]):
pass
# i expect static analyzer to show:
foo(42) # not OK
foo(A()) # not OK
foo(B()) # not OK
foo(C()) # OK
foo(D()) # OK
foo(E()) # OK
use CLASS_NAME.__mro__ to get a tuple of all the parent class(including itself) of that class and use type(instance_name) to get the class of an instance. from this you can create a set data structure and find what you want.
>>> class A:
pass
>>> class B:
pass
>>> class C(A,B):
pass
>>> c = C()
>>> type(c).__mro__
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)

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.

Class attributes with a "calculated" name

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?.

Categories