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

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

Related

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)

Store instance of class A in instance of class B

I have a question which is more regarding OOP in general rather than python specific.
Is ist possible to store instances of ClassA in instance of ClassB without a specific method, i.e. by some kind of inheritance.
Example: let's say I have one Model class and one Variable class
class Model():
def __init__(self):
self.vars = []
def _update_vars(self,Variable):
self.vars.append(Variable)
class Variable(Model):
def __init__(self,**kwargs):
self.__dict__.update(kwargs)
Is it now possible to call _update_vars whenever an instance of variable is being created.
So if I do something like this:
mdl = Model()
varA = Variable(...)
varB = Variable(...)
that mdl.vars would now include varA and varB.
I know that I could easily do this by passing the variables as an argument to a "public" method of Model. So I am not looking for
mdl.update_vars(varA)
So my two questions are:
is this possible?
if yes: would this very non-standard OOP programming?
Thanks for your help!
That's not how class inheritance is supposed to work. You only want to inherit something if the child class is going to make use of a good amount of the attributes/methods within the parent class. If the child class has a markedly different structure it should be a class of its own.
In either case, as mentioned by #jasonharper, at some point you would need to give direction as to which Variable instance belongs in which Model instance, so you're likely to end up with something like these:
varA = Variable(mdl, ...)
# or this
mdl.varA = Variable(...)
With the first way, you would maintain the method on your Variable class:
class Foo:
def __init__(self):
self.vars = []
class Bar:
def __init__(self, foo_instance, **kwargs):
foo_instance.vars.append(self)
f = Foo()
b = Bar(f, hello='hey')
f.vars
# [<__main__.Bar object at 0x03F6B4B0>]
With the second way, you can append the Variable instances into a list each time it's added:
class Foo:
def __init__(self):
self.vars = []
def __setattr__(self, name, val):
self.__dict__.update({name: val})
if not name == 'vars': # to prevent a recursive loop
self.vars.append(val)
f = Foo()
f.vars
# []
f.a = 'bar'
f.vars
# ['bar']
Of course, an easier way would be to just look directly into the __dict__ each time you want vars:
class Bar:
#property
def vars(self):
# Or you can return .items() if you want both the name and the value
return list(self.__dict__.values())
b = Bar()
b.a = 'hello'
b.vars
# ['hello']
Both of these will work the same even if you assigned the attributes with your own class instances.
You can use super() for this and pass the instance to the parent
class Model():
vars = []
def __init__(self, other=None):
if other:
self.vars.append(other)
class Variable(Model):
def __init__(self, a):
self.a = a
super().__init__(self)
mdl = Model()
varA = Variable(3)
varB = Variable(4)
print(mdl.vars)

How to know from which class an attribute is coming

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'>

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.

What is the difference between declaring data attributes inside or outside __init__ [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Python: Difference between class and instance attributes
I'm trying to get my head around OOP in Python and I'm a bit confused when it comes to declare variables within a class. Should I declare them inside of the __init__ procedure or outside it? What's the difference?
The following code works just fine:
# Declaring variables within __init__
class MyClass:
def __init__(self):
country = ""
city = ""
def information(self):
print "Hi! I'm from %s, (%s)"%(self.city,self.country)
me = MyClass()
me.country = "Spain"
me.city = "Barcelona"
me.information()
But declaring the variables outside of the __init__ procedure also works:
# Declaring variables outside of __init__
class MyClass:
country = ""
city = ""
def information(self):
print "Hi! I'm from %s, (%s)"%(self.city,self.country)
me = MyClass()
me.country = "Spain"
me.city = "Barcelona"
me.information()
In your first example you are defining instance attributes. In the second, class attributes.
Class attributes are shared between all instances of that class, where as instance attributes are "owned" by that particular instance.
Difference by example
To understand the differences let's use an example.
We'll define a class with instance attributes:
class MyClassOne:
def __init__(self):
self.country = "Spain"
self.city = "Barcelona"
self.things = []
And one with class attributes:
class MyClassTwo:
country = "Spain"
city = "Barcelona"
things = []
And a function that prints out information about one of these objects:
def information(obj):
print "I'm from {0}, ({1}). I own: {2}".format(
obj.city, obj.country, ','.join(obj.things))
Let's create 2 MyClassOne objects and change one to be Milan, and give Milan "something":
foo1 = MyClassOne()
bar1 = MyClassOne()
foo1.city = "Milan"
foo1.country = "Italy"
foo1.things.append("Something")
When we call information() on the foo1 and bar1 we get the values you'd expect:
>>> information(foo1)
I'm from Milan, (Italy). I own: Something
>>> information(bar1)
I'm from Barcelona, (Spain). I own:
However, if we were to do exactly the same thing, but using instances of MyClassTwo you'll see that the class attributes are shared between instances.
foo2 = MyClassTwo()
bar2 = MyClassTwo()
foo2.city = "Milan"
foo2.country = "Italy"
foo2.things.append("Something")
And then call information()...
>>> information(foo2)
I'm from Milan, (Italy). I own: Something
>>> information(bar2)
I'm from Barcelona, (Spain). I own: Something
So as you can see - things is being shared between the instances. things is a reference to a list that each instance has access to. So if you append to things from any instance that same list will be seen by all other instances.
The reason you don't see this behaviour in the string variables is because you are actually assigning a new variable to an instance. In this case that reference is "owned" by the instance and not shared at the class level. To illustrate let's assign a new list to things for bar2:
bar2.things = []
This results in:
>>> information(foo2)
I'm from Milan, (Italy). I own: Something
>>> information(bar2)
I'm from Barcelona, (Spain). I own:
You're two versions of the code are very different. In python, you have 2 distinct entities: classes and class instances. An instance is what is created when you do:
new_instance = my_class()
You can bind attributes to an instance within __init__ via self (self is the new instance).
class MyClass(object):
def __init__(self):
self.country = "" #every instance will have a `country` attribute initialized to ""
There's nothing terribly special about self and __init__. self is the customary name that is used to represent the instance that gets passed to every method (by default).
a.method() #-> Inside the class where `method` is defined, `a` gets passed in as `self`
The only thing special here is that __init__ gets called when the class is constructed:
a = MyClass() #implicitly calls `__init__`
You can also bind attributes to the class (putting it outside __init__):
class MyClass(object):
country = "" #This attribute is a class attribute.
At any point, you can bind a new attribute to an instance simply by:
my_instance = MyClass()
my_instance.attribute = something
Or a new attribute to a class via:
MyClass.attribute = something
Now it gets interesting. If an instance doesn't have a requested attribute, then python looks at the class for the attribute and returns it (if it is there). So, class attributes are a way for all instances of a class to share a piece of data.
Consider:
def MyClass(object):
cls_attr = []
def __init__(self):
self.inst_attr = []
a = MyClass()
a.inst_attr.append('a added this')
a.cls_attr.append('a added this to class')
b = MyClass()
print (b.inst_attr) # [] <- empty list, changes to `a` don't affect this.
print (b.cls_attr) # ['a added this to class'] <- Stuff added by `a`!
print (a.inst_attr) #['a added this']
When you define a variable in class scope (outside any method), it becomes a class attribute. When you define a value in method scope, it becomes a method local variable. If you assign a value to an attribute of self (or any other label referencing an object), it becomes (or modifies) an instance attribute.

Categories