How to know from which class an attribute is coming - python

Imagine an instance from a classes has an attribute attr. I want to know from where it got that attribute.
Let's consider those 2 cases in Python :
class vehicle():
speed = 5
class flying_machine(vehicle):
density = 1
And
class vehicle():
def __init__(self):
self.speed = 5
class flying_machine(vehicle):
def __init__(self):
super().__init__()
self.density = 1
If you execute the following line of codes :
b = flying_machine()
s = b.speed
s has the value 5. Both cases are different, in the first case, speed is an attribute of the class "vehicle" and in the other case, it's a attribute of an instance of the class "vehicle".
My problem is the same in both case, i want to know from where b got his attribute "speed", I would like to have a function that i could call on b and "speed" and would point me to the place where b.speed was defined.
In my example, it's easy to see where b got its attribute from, but you could imagine classes and subclasses being defined across multiple documents, and having a long inheritance chain. For example :
# doc1.py
class vehicle():
speed = 5
# doc2.py
from doc1.py import *
class flying_machine(vehicle):
density = 1
# doc3.py
from doc2.py import *
class space_machine(flying_machine):
light_speed = 0.5
# main.py
from doc3.py import *
c = space_machine()
In this last case, my c has an attribute speed, and if I want to see where its coming from, i have to go through all previous import, and that's where it would be useful to have a function to show me that c.speed comes from doc1.py .

Generally, I would advise you to use a proper IDE like PyCharm that allows you to Ctrl+click on any attribute and that will navigate you to where that attribute is assigned.
That being said, you can use a class's mro to dynamically extract some information about where attributes may originate by inspecting the instance's and its classes' __dict__ and __slots__:
def lookup(obj, attr):
if attr in obj.__dict__ or attr in obj.__slots__:
print('{}: instance attribute'.format(attr))
klass = next((k for k in obj.__class__.mro()
if '__slots__' in k.__dict__ and attr in k.__slots__),
None)
if klass:
print('but expected for class {}'.format(klass))
else:
for klass in obj.__class__.mro():
if attr in klass.__dict__:
print('{}: class attribute of {}'.format(attr, klass))
You can use this function for arbitrary objects and attributes:
class Vehicle:
speed = 5
class Ugly:
__slots__ = ('look',)
def __init__(self):
self.look = 'terrible'
class Car(Ugly, Vehicle):
def __init__(self):
super().__init__()
self.num_wheels = 4
>>> c = Car()
>>> lookup(c, 'speed')
speed: class attribute of <class '__main__.Vehicle'>
>>> lookup(c, 'num_wheels')
num_wheels: instance attribute
>>> lookup(c, 'look')
look: instance attribute
but expected for class <class '__main__.Ugly'>

Related

How to define different addresses for class attributes and instance attributes?

How to define different addresses for class attributes and instance attributes?
This problem has bothered me for a long time, unless I delete the definition of the class attribute, but want to use the class attribute.
I have defined a dict with the same name in the class attribute and instance attribute. How can I make the memory address different? I tried a variety of methods to delete the content of the class attribute. Is there any other method?
My demo code is as follows:
class MyClass:
bar: dict = {}
def __init__(self):
bar: dict = {}
print(id(MyClass.bar))
a = MyClass()
print(id(a.bar))
1914627629760
1914627629760
class MyClass:
bar: dict = {}
def __init__(self):
self.bar = {}
print(id(MyClass.bar))
a = MyClass()
print(id(a.bar))
2318292079808
2318295104384
That said, I have no idea why we are doing this, and there is an almost 100% chance this will make whomever (next) maintains this codebase go insane within the next 2 years.
Explanation:
You are not "saving" your variable in your __init__() function.
Try running:
class MyClass:
def __init__(self):
self.a = 1 # setting attribute a to value 1
b = 2 # b is not an attribute, it's just a local variable
m = MyClass()
print(m.a) # this will work
print(m.b) # this will not

Python: access a parent attribute from the child class

In Python, I have the following code that came up as a quiz question:
class Big_Cat:
def __init__(self):
self.x = "dangerous"
class Cat(Big_Cat):
def __init__(self):
self.y = "quiet"
new_cat = Cat()
print(new_cat.x, new_cat.y)
Since the cat class is inheriting from the BigCat class, it should also have access to variable x. Then why is it throwing an error on the print screen line. How else can new_cat get access the variable x from parent?
After inheriting from the super class, you must call the parent's __init__ (constructor). You can get a reference to the parent class by using super().
Here is an example:
class Big_Cat:
def __init__(self):
self.x = "dangerous"
class Cat(Big_Cat):
def __init__(self):
super().__init__()
self.y = "quiet"
new_cat = Cat()
print(new_cat.x, new_cat.y)
Output:
dangerous quiet
You can use super to call parent class' __init__
In [1829]: class Big_Cat:
...: def __init__(self):
...: self.x = "dangerous"
...:
...: class Cat(Big_Cat):
...: def __init__(self):
...: super(Cat, self).__init__()
...: self.y = "quiet"
...:
...: new_cat = Cat()
In [1830]: new_cat.x
Out[1830]: 'dangerous'
In Python there is a different approach than in true OOP languages as C++ or Java.
There is no such thing as declaring an attribute in a direct way in a class definition so that this attribute will become automatically the instance's attribute:
class A:
an_attribute = 0
The an_attribute is an attribute of the class A, but not an attribute of instances of this class:
a = A() # an instance of the class A
print(a.an_attribute) # 0 - so IS the an_attribute an instance's attribute?
It seems that an_attribute is the instance's attribute, but ...
A.an_attribute = 100 # changing the value of a CLASS attribute
print(a.an_attribute) # 100; so it is NOT the independent OBJECT 's attribute
So how to create an object's attribute? Very easy:
a.an_attribute = 200 # creating an OBJECT's attribute
print(a.an_attribute) # 200 — the OBJECT's attribute, independent of a CLASS' one
print(A.an_attribute) # 100 — the CLASS attribute
From this moment the object a has its own attribute, different from the class attribute of the same name.
It means that different instances of the same class may have not only different values of the same attributes, but even totally different attributes:
b = A()
b.different_attribute = 500
Very weird situation:
the object a has the attribute an_attribute, but the object b of the same class not,
the object b has the attribute different_attribute , but the object a not.
Is there a way to prescribe / initialize instances' attributes in a class definition?
Luckily, there is a special method __init__(), which runs automatically when you create an instance of a class, and which automatically receives just created object as its first parameter (commonly named as this).
So you may assign to just created object an attribute by using this automatically filled parameter:
class A:
def __init__(self):
self.an_attribute = 20
self.different_attribute = 50
Now all new instances of the class A will have their own, object's attributes an_attribute and different_attribute (initialized with values 20 and 50, respectively, which is not important here).
So, instance variables are not automatically inherited by a subclass. Other people already explained, how to go around it — not surprisingly in the __init__() method of a subclass with the help of the super() built-in function.
You need to call the constructor of the parent class inside the constructor of the child class in order for the child class to access the methods and attributes of the parent class. You can do so with the help of super() method.
class Big_Cat:
def __init__(self):
self.x = "dangerous"
class Cat(Big_Cat):
def __init__(self):
super().__init__()
self.y = "quiet"
new_cat = Cat()
print(new_cat.x, new_cat.y)

how to know from which class instance a function is called to access the instance attributes

I want to access an attribute of the class instance that called a function :
for example:
class A:
def a(self):
return B.q
class B:
q=0
def b(self):
M=A()
return M.a()
c=B()
c.q = 6
print(c.b())
the output will be 0 but I want it to print the q attribute of the instance c of the class B which has the value 6
Pass the instance as a parameter.
class A:
def a(self, b):
return b.q
class B:
q=0
def b(self):
M=A()
return M.a(self)
c=B()
c.q = 6
print(c.b())
This appears to be very bad program design. What are you trying to accomplish with this?
You have a class attribute and an instance attribute -- in that class -- of the same name, q. This makes your code difficult to follow and to maintain.
You have method B.b instantiate an instance of class A. You immediately call A.a, which has been assigned the questionable task of returning an instance attribute from and object of class B.
Clean up your design.
Use init appropriately for each class.
Design your class methods to work appropriately with the characteristics of instances of that class. Your question strongly suggests that your design is not yet clean in your mind, nor in code.
define an init method so that you can work with the instance attributes instead of the class variable
class A:
def a(self):
return B.q
class B:
def __init__(self):
self.q = 0
def b(self):
M=A()
return M.a()
c=B()
c.q = 6
print(c.b())

How to get object attributes to update dynamically in Python

I'd like to create a class that has 2 input attributes and 1 output attribute such that whenever one of the input attributes are modified the output attribute is modified automatically
I've tried defining the attributes as instance variables within and outside the constructor function but in either case, after instantiating the object, the output attribute remains fixed at the value set at the moment of instantiation
class Example():
def __init__(self,n):
self.name=n
inA=1
inB=1
if inA==1 and inB==1:
outA=1
else:
outA=0
when instantiated outA is set to 1 as expected
but if I try to update:
object.inA=0
object.outA remains 1 whereas I need it to be updated to 0
Trying to avoid the use of functions if possible. New to python and OOP so sorry if this question is nonsensical or has an obvious answer
If you want instance attributes that depend on other instance attributes, properties are the way to go.
class Example:
def __init__(self, n):
self.name = n
self.inA = 1
self.inB = 1
#property
def outA(self):
return self.inA and self.inB
You access outA like a regular instance attribute, obj.outA.
>>> my_obj = Example("example")
>>> my_obj.outA
1
Changing the attributes inA and inB affect outA.
>>> my_obj.inA = 0
>>> my_obj.outA
0
You can create a function in the class and some other minor changes:
class Example():
def __init__(self,n):
self.name=n
self.inA=1
self.inB=1
def f(self):
if self.inA==1 and self.inB==1:
self.outA=1
else:
self.outA=0
To call it:
a = Example('foo')
a.inA = 0
a.f()
print(a.outA)
Output:
0
As you can see, taking out:
a.f()
line would make it give an error:
AttributeError: 'Example' object has no attribute 'outA'
Do you want it to return your output?
Expanding on U9-Forward's answer:
class Example():
def __init__(self,n):
self.name = n
self.inA = 1
self.inB = 1
def f(self):
return self.inA and self.inB

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.

Categories