Python reverse introspection - python

Lets suppose this example: Two siblings classes where one loads the other class as a new attribute and then i wish to use this attribute from the main class inside the sibling.
a = 2
class AN(object):
def __init__(self,a):
self.aplus = a + 2
self.BECls = BE(a)
class BE(object):
def __init__(self,a):
print a
def get_aplus(self):
????
c = AN(a)
and i'd like to do:
c.BECls.get_aplus()
and this shall return something like self.self.aplus (metaphorically), that would be 4
Resuming: get aplus attribute from AN inside BE class, without declaring as arguments, but doing a "Reverse introspection", if it possible, considering the 'a' variable must be already loaded trough AN.
Sorry if I not made myself clear but I've tried to simplify what is happening with my real code.
I guess the problem may be the technique i'm using on the classes. But not sure what or how make it better.
Thanks

OP's question:
get aplus attribute from AN inside BE class, without declaring as
arguments, but doing a "Reverse introspection", if it possible,
considering the 'a' variable must be already loaded trough AN.
The closest thing we have to "reverse introspection" is a search through gc.getreferrers().
That said, it would be better to simply make the relationship explicit
class AN(object):
def __init__(self,a):
self.aplus = a + 2
self.BECls = BE(self, a)
class BE(object):
def __init__(self, an_obj, a):
self.an_obj = an_obj
print a
def get_aplus(self):
return self.an_obj.aplus
if __name__ == '__main__':
a = 2
c = AN(a)
print c.BECls.get_aplus() # this returns 4

Related

self in Python classes

I know first argument in Python methods will be an instance of this class. So we need use "self" as first argument in methods. But should we also specify attribures (variables) in method starting with "self."?
My method work even if i don't specify self in his attributes:
class Test:
def y(self, x):
c = x + 3
print(c)
t = Test()
t.y(2)
5
and
class Test:
def y(self, x):
self.c = x + 3
print(self.c)
t = Test()
t.y(2)
5
For what i would need specify an attribute in methods like "self.a" instead of just "a"?
In which cases first example will not work but second will? Want to see situation which shows really differences between two of them, because now they behave the same from my point of view.
The reason you do self.attribute_name in a class method is to perform computation on that instances attribute as opposed to using a random variable.For Example
class Car:
def __init__(self,size):
self.size = size
def can_accomodate(self,number_of_people):
return self.size> number_of_people
def change_size(self,new_size):
self.size=new_size
#works but bad practice
def can_accomodate_v2(self,size,number_of_people):
return size> number_of_people
c = Car(5)
print(c.can_accomodate(2))
print(c.can_accomodate_v2(4,2))
In the above example you can see that the can_accomodate use's self.size while can_accomodate_v2 passes the size variable which is bad practice.Both will work but the v2 is a bad practice and should not be used.You can pass argument into a class method not related to the instance/class for example "number_of_people" in can_accomodate funtion.
Hope this helps.

Order of classes matters when using variables from each other

I am new to Python and didn't find an answer to the following problem:
I have two classes and want them to use variables from each other. Is there a simple way to do this because if I do it like this class a does not know that class b exists.
class a:
y=1
print(b.x)
class b:
x=1
print(a.y)
And how do I use overwrite the variables, the following code does not work:
class a:
y=b.x
class b:
x=1
You are executing print as part of the class definition. It executes as soon as python sees that line of code, before it's read the part about class b.
Instead, use functions inside the classes to execute code after the classes have been defined:
class a:
y=1
def go():
print(b.x)
class b:
x=1
def go():
print(a.y)
a.go()
b.go()
As I said in a comment, your code isn't making effective use of classes. Here's what I think would be better approach that offers more flexibility in working around the circular reference issue.
First the class definitions (which follow the PEP 8 naming convention guidelines):
class A:
def __init__(self, value, linked_value=None):
self.y = value
if isinstance(linked_value, B):
self.linked_value = linked_value.x
def print_linked_value(self):
print(self.linked_value)
class B:
def __init__(self, value, linked_value=None):
self.x = value
if isinstance(linked_value, A):
self.linked_value = linked_value.y
def print_linked_value(self):
print(self.linked_value)
Definitions like that provide two ways to set up the circular references:
By creating them separately, then explicitly linking them:
# First create instances of each class.
a = A(1)
b = B(42)
# Then link them.
a.linked_value = b.x
b.linked_value = a.y
a.print_linked_value() # -> 42
b.print_linked_value() # -> 1
*OR* by creating the first one without a linked value and leaving only the second needing to be linked manually.
# First create instances of each class, but link the second to the first
# when it's created.
a = A(1)
b = B(42, a) # Create and link to first.
# Then link the first to the second to complete the circular references.
a.linked_value = b.x
# Same result.
a.print_linked_value() # -> 42
b.print_linked_value() # -> 1
Final note: Another, more advanced alternative that can also be applied in situations like this by using the built-in property() function as a decorator to create "descriptors". Here's an answer to a somewhat related question that illustrating its use.
class A:
y = 1
def foo(self):
print B.x
class B:
x = 1
def bar(self):
print A.y
>>> A().foo()
2
>>> B().bar()
1
Use 'print' in some function definition.

how to dynamically generate a subclass in a function?

I'm attempting to write a function that creates a new subclass named with the string it gets passed as an argument. I don't know what tools would be best for this, but I gave it a shot in the code below and only managed to make a subclass named "x", instead of "MySubClass" as intended. How can I write this function correctly?
class MySuperClass:
def __init__(self,attribute1):
self.attribute1 = attribute1
def makeNewClass(x):
class x(MySuperClass):
def __init__(self,attribute1,attribute2):
self.attribute2 = attribute2
x = "MySubClass"
makeNewClass(x)
myInstance = MySubClass(1,2)
The safest and easiest way to do this would be to use the type builtin function. This takes an optional second argument (tuple of base classes), and third argument (dict of functions). My recommendation would be the following:
def makeNewClass(x):
def init(self,attribute1,attribute2):
# make sure you call the base class constructor here
self.attribute2 = attribute2
# make a new type and return it
return type(x, (MySuperClass,), {'__init__': init})
x = "MySubClass"
MySubClass = makeNewClass(x)
You will need to populate the third argument's dict with everything you want the new class to have. It's very likely that you are generating classes and will want to push them back into a list, where the names won't actually matter. I don't know your use case though.
Alternatively you could access globals and put the new class into that instead. This is a really strangely dynamic way to generate classes, but is the best way I can think of to get exactly what you seem to want.
def makeNewClass(x):
def init(self,attribute1,attribute2):
# make sure you call the base class constructor here
self.attribute2 = attribute2
globals()[x] = type(x, (MySuperClass,), {'__init__': init})
Ryan's answer is complete, but I think it's worth noting that there is at least one other nefarious way to do this besides using built-in type and exec/eval or whatever:
class X:
attr1 = 'some attribute'
def __init__(self):
print 'within constructor'
def another_method(self):
print 'hey, im another method'
# black magics
X.__name__ = 'Y'
locals()['Y'] = X
del X
# using our class
y = locals()['Y']()
print y.attr1
y.another_method()
Note that I only used strings when creating class Y and when initializing an instance of Y, so this method is fully dynamic.

class initialization in Python

I found that some classes contain a __init__ function, and some don’t. I’m confused about something described below.
What is the difference between these two pieces of code:
class Test1(object):
i = 1
and
class Test2(object):
def __init__(self):
self.i = 1
I know that the result or any instance created by these two class and the way of getting their instance variable are pretty much the same. But is there any kind of “default” or “hidden” initialization mechanism of Python behind the scene when we don’t define the __init__ function for a class? And why I can’t write the first code in this way:
class Test1(object):
self.i = 1
That’s my questions. Thank you very much!
Thank you very much Antti Haapala! Your answer gives me further understanding of my questions. Now, I understand that they are different in a way that one is a "class variable", and the other is a "instance variable". But, as I tried it further, I got yet another confusing problem.
Here is what it is. I created 2 new classes for understanding what you said:
class Test3(object):
class_variable = [1]
def __init__(self):
self.instance_variable = [2]
class Test4(object):
class_variable = 1
def __init__(self):
self.instance_variable = 2
As you said in the answer to my first questions, I understand the class_variable is a "class variable" general to the class, and should be passed or changed by reference to the same location in the memory. And the instance_variable would be created distinctly for different instances.
But as I tried out, what you said is true for the Test3's instances, they all share the same memory. If I change it in one instance, its value changes wherever I call it.
But that's not true for instances of Test4. Shouldn't the int in the Test4 class also be changed by reference?
i1 = Test3()
i2 = Test3()
>>> i1.i.append(2)
>>> i2.i
[1, 2]
j1 = Test4()
j2 = Test4()
>>> j1.i = 3
>>> j2.i
1
Why is that? Does that "=" create an "instance variable" named "i" without changing the original "Test4.i" by default? Yet the "append" method just handles the "class variable"?
Again, thank you for your exhaustive explanation of the most boring basic concepts to a newbie of Python. I really appreciate that!
In python the instance attributes (such as self.i) are stored in the instance dictionary (i.__dict__). All the variable declarations in the class body are stored as attributes of the class.
Thus
class Test(object):
i = 1
is equivalent to
class Test(object):
pass
Test.i = 1
If no __init__ method is defined, the newly created instance usually starts with an empty instance dictionary, meaning that none of the properties are defined.
Now, when Python does the get attribute (as in print(instance.i) operation, it first looks for the attribute named i that is set on the instance). If that fails, the i attribute is looked up on type(i) instead (that is, the class attribute i).
So you can do things like:
class Test:
i = 1
t = Test()
print(t.i) # prints 1
t.i += 1
print(t.i) # prints 2
but what this actually does is:
>>> class Test(object):
... i = 1
...
>>> t = Test()
>>> t.__dict__
{}
>>> t.i += 1
>>> t.__dict__
{'i': 2}
There is no i attribute on the newly created t at all! Thus in t.i += 1 the .i was looked up in the Test class for reading, but the new value was set into the t.
If you use __init__:
>>> class Test2(object):
... def __init__(self):
... self.i = 1
...
>>> t2 = Test2()
>>> t2.__dict__
{'i': 1}
The newly created instance t2 will already have the attribute set.
Now in the case of immutable value such as int there is not that much difference. But suppose that you used a list:
class ClassHavingAList():
the_list = []
vs
class InstanceHavingAList()
def __init__(self):
self.the_list = []
Now, if you create 2 instances of both:
>>> c1 = ClassHavingAList()
>>> c2 = ClassHavingAList()
>>> i1 = InstanceHavingAList()
>>> i2 = InstanceHavingAList()
>>> c1.the_list is c2.the_list
True
>>> i1.the_list is i2.the_list
False
>>> c1.the_list.append(42)
>>> c2.the_list
[42]
c1.the_list and c2.the_list refer to the exactly same list object in memory, whereas i1.the_list and i2.the_list are distinct. Modifying the c1.the_list looks as if the c2.the_list also changes.
This is because the attribute itself is not set, it is just read. The c1.the_list.append(42) is identical in behaviour to
getattr(c1, 'the_list').append(42)
That is, it only tries read the value of attribute the_list on c1, and if not found there, then look it up in the superclass. The append does not change the attribute, it just changes the value that the attribute points to.
Now if you were to write an example that superficially looks the same:
c1.the_list += [ 42 ]
It would work identical to
original = getattr(c1, 'the_list')
new_value = original + [ 42 ]
setattr(c1, 'the_list', new_value)
And do a completely different thing: first of all the original + [ 42 ] would create a new list object. Then the attribute the_list would be created in c1, and set to point to this new list. That is, in case of instance.attribute, if the attribute is "read from", it can be looked up in the class (or superclass) if not set in the instance, but if it is written to, as in instance.attribute = something, it will always be set on the instance.
As for this:
class Test1(object):
self.i = 1
Such thing does not work in Python, because there is no self defined when the class body (that is all lines of code within the class) is executed - actually, the class is created only after all the code in the class body has been executed. The class body is just like any other piece of code, only the defs and variable assignments will create methods and attributes on the class instead of setting global variables.
I understood my newly added question. Thanks to Antti Haapala.
Now, when Python does the get attribute (as in print(instance.i) operation, it first looks for the attribute named i that is set on the instance). If that fails, the i attribute is looked up on type(i) instead (that is, the class attribute i).
I'm clear about why is:
j1 = Test4()
j2 = Test4()
>>> j1.i = 3
>>> j2.i
1
after few tests. The code
j1.3 = 3
actually creates a new instance variable for j1 without changing the class variable. That's the difference between "=" and methods like "append".
I'm a newbie of Python coming from c++. So, at the first glance, that's weird to me, since I never thought of creating a new instance variable which is not created in the class just using the "=". It's really a big difference between c++ and Python.
Now I got it, thank you all.

How to dynamically compose and access class attributes in Python? [duplicate]

This question already has answers here:
How to access (get or set) object attribute given string corresponding to name of that attribute
(3 answers)
Closed 3 years ago.
I have a Python class that have attributes named: date1, date2, date3, etc.
During runtime, I have a variable i, which is an integer.
What I want to do is to access the appropriate date attribute in run time based on the value of i.
For example,
if i == 1, I want to access myobject.date1
if i == 2, I want to access myobject.date2
And I want to do something similar for class instead of attribute.
For example, I have a bunch of classes: MyClass1, MyClass2, MyClass3, etc. And I have a variable k.
if k == 1, I want to instantiate a new instance of MyClass1
if k == 2, I want to instantiate a new instance of MyClass2
How can i do that?
EDIT
I'm hoping to avoid using a giant if-then-else statement to select the appropriate attribute/class.
Is there a way in Python to compose the class name on the fly using the value of a variable?
You can use getattr() to access a property when you don't know its name until runtime:
obj = myobject()
i = 7
date7 = getattr(obj, 'date%d' % i) # same as obj.date7
If you keep your numbered classes in a module called foo, you can use getattr() again to access them by number.
foo.py:
class Class1: pass
class Class2: pass
[ etc ]
bar.py:
import foo
i = 3
someClass = getattr(foo, "Class%d" % i) # Same as someClass = foo.Class3
obj = someClass() # someClass is a pointer to foo.Class3
# short version:
obj = getattr(foo, "Class%d" % i)()
Having said all that, you really should avoid this sort of thing because you will never be able to find out where these numbered properties and classes are being used except by reading through your entire codebase. You are better off putting everything in a dictionary.
For the first case, you should be able to do:
getattr(myobject, 'date%s' % i)
For the second case, you can do:
myobject = locals()['MyClass%s' % k]()
However, the fact that you need to do this in the first place can be a sign that you're approaching the problem in a very non-Pythonic way.
OK, well... It seems like this needs a bit of work. Firstly, for your date* things, they should be perhaps stored as a dict of attributes. eg, myobj.dates[1], so on.
For the classes, it sounds like you want polymorphism. All of your MyClass* classes should have a common ancestor. The ancestor's __new__ method should figure out which of its children to instantiate.
One way for the parent to know what to make is to keep a dict of the children. There are ways that the parent class doesn't need to enumerate its children by searching for all of its subclasses but it's a bit more complex to implement. See here for more info on how you might take that approach. Read the comments especially, they expand on it.
class Parent(object):
_children = {
1: MyClass1,
2: MyClass2,
}
def __new__(k):
return object.__new__(Parent._children[k])
class MyClass1(Parent):
def __init__(self):
self.foo = 1
class MyClass2(Parent):
def __init__(self):
self.foo = 2
bar = Parent(1)
print bar.foo # 1
baz = Parent(2)
print bar.foo # 2
Thirdly, you really should rethink your variable naming. Don't use numbers to enumerate your variables, instead give them meaningful names. i and k are bad to use as they are by convention reserved for loop indexes.
A sample of your existing code would be very helpful in improving it.
to get a list of all the attributes, try:
dir(<class instance>)
I agree with Daenyth, but if you're feeling sassy you can use the dict method that comes with all classes:
>>> class nullclass(object):
def nullmethod():
pass
>>> nullclass.__dict__.keys()
['__dict__', '__module__', '__weakref__', 'nullmethod', '__doc__']
>>> nullclass.__dict__["nullmethod"]
<function nullmethod at 0x013366A8>

Categories