i have a question about class attribute in python.
class base :
def __init__ (self):
pass
derived_val = 1
t1 = base()
t2 = base()
t2.derived_val +=1
t2.__class__.derived_val +=2
print t2.derived_val # its value is 2
print t2.__class__.derived_val # its value is 3
The results are different. I also use id() function to find t2.derived_val and t2.__class__.derived_val have different memory address.
My problem is derived_val is class attribute. Why it is different in above example?
Is it because the instance of class copy its own derived_val beside the class attribute?
There are class attributes, and instance attributes.
When you say
class base :
derived_val = 1
You are defining a class attribute. derived_val becomes a key in
base.__dict__.
t2=base()
print(base.__dict__)
# {'derived_val': 1, '__module__': '__main__', '__doc__': None}
print(t2.__dict__)
# {}
When you say t2.derived_val Python tries to find 'derived_val' in t2.__dict__. Since it is not there, it looks if there is a 'derived_val' key in any of t2's base classes.
print(t2.derived_val)
print(t2.__dict__)
# 1
# {}
But when you assign a value to t2.derived_val, you are now adding an instance attribute to t2. A derived_val key is added to t2.__dict__.
t2.derived_val = t2.derived_val+1
print(t2.derived_val)
print(t2.__dict__)
# 2
# {'derived_val': 2}
Note that at this point, there are two derived_val attributes, but only
the instance attribute is easily accessible. The class attribute becomes accessible only through referencing base.derived_val or direct access to the class dict base.__dict__.
Check it out here and here.
The __class__ attribute is the class that the object belongs to. So in your example, the situation is similar to static variables. The t2.__class__.derived_val is referring to the variable that belongs to the class, not the variable that belongs to t2.
Another way (perhaps a more concise one) to demonstrate this:
class A():
a1 = []
x = A()
y = A()
x.a1.append("test")
x.a1, y.a1
(['test'], ['test'])
class B():
b1 = None
def __init__(self):
self.b1 = list()
r = B()
s = B()
r.b1.append("test")
r.b1, s.b1
(["test"], [])
Related
Look at the code below.
class A :
def __init__(self, a = "Hello") :
self.a = a
print(A().a) # 1
print(A.a) # 2
1 is not error
2 is error - AttributeError: type object 'A' has no attribute 'a'
What is the difference between the two results?
In your code A refers the the type of a class and also to its constructor/initialiser. A is called the class and when you construct an object of type A with the constructor you get an instance of that class.
A # Refers to the class A
A() # is an instance of class A
There is a difference between a class property and an instance property. Consider the following code:
class A:
propertyA = "hello"
def __init__(self, string="world"):
self.propertyB = string
In this snippet propertyA is a class property while propertyB is an instance property. Each instance of type A has its own propertyB and you must instantiate and object (an instance) first.
A.propertyA # Class property, does not need an instance
A().propertyB # instance property, needs an instance
In your code the constructor for A is the code written in the __init__. This code will be called when you type A(). Note that you specified a default value for the parameter a but if you don't you would call the constructor like this:
A("hello") # or:
A(a="hello")
Note that classes, instances and constructors are fundamentals of OOP (and by extension Python), you really should learn this, it avoids lots of basic errors.
You need to create an instance of the class first:
class A :
def __init__(self, a = "Hello") :
self.a = a
class_instance = A()
print(class_instance.a)
You can set the value of "a" when creating the instance by typing in the parenthesis:
class_instance = A("this is the value of a")
you can change the value after the creation like so:
class_instance.a = "New value of a"
A().a is creating an instance and returns the a value of the instance.
A.a cannot be executed because A is the Class name and doesn't have any attributes if you don't create an instance first
I am trying to load a whole class instance via dill rather than dump and load each class variable one at a time.
Can anybody show me how to do this:
class Object(object):
pass
class ClassA:
def __init__(self):
self.DATA = "Initial"
class ClassB:
def __init__(self, CA):
self.CA = CA
def updateValue(self):
#a = dill.load(ClassA.storage)
a = Object()
a.DATA = "new value"
self.CA = a
print self.CA.DATA
CA = ClassA()
CB = ClassB(CA)
CB.updateValue()
print CA.DATA
So that the output is:
new value
new value
I think you're asking:
Given object A and object B, how can I copy all of A's attributes to B in one step (or programatically)?
Naive approach:
B.__dict__ = dict(A.__dict__) # make a copy so objects don't share same dict
The problem with this approach is that it clobbers any preexisting attributes in B that did not exist in A. eg.
B.attr = "some value"
B.__dict__ = dict(A.__dict__)
print(hasattr(B, "attr")) # expect False
A better approach. This approach copies over A's attributes and leaves any attributes that exist on B, but not on A, alone.
B.__dict__.update(A.__dict__)
However, there are still problems if there are attributes on A's class and its parent classes that you want to copy over. But I think that's a different question.
In your updateValue
def updateValue(self):
self.ca.DATA = "new value"
I have example class:
class A():
other_attribute = 2
def __init__(self):
setattr(A,"my_attribute",1)
a = A()
How can I delete my_attribute and other_attribute from instance?
PS. I edited code to better explain problem. For example I have class, which dynamically adds attributes
class A():
def __init__(self, attribute_name, attribute_value):
setattr(A, attribute_name, attribute_value)
a = A("my_attribute", 123)
I created my_attribute, in instance a, but then I do not need it anymore. But at other instances are other attributes, which I do not want to change.
other_attribute and my_attribute are not an attributes on the instance. They are attributes on the class. You'd have to delete attributes from there, or provide an instance attribute with the same name (and a different value) to mask the class attribute.
Deleting the attributes from the class means that they'll not be available anymore on any instance.
You cannot 'delete' class attributes on individual instances. If an attribute is not to be shared by all instances, don't make them class attributes.
other_attribute is shared by all instances of A, that means it is a part of
A.__dict__
dictionary. You can do this for one instance of a class if you initialize an attribute in the constructor:
class A:
def __init__(self):
self.attrib = 2
self.attrib2 = 3
a = A()
print "Before " + str(a.__dict__)
del a.__dict__["attrib"];
print "After " + str(a.__dict__)
Output is:
Before {'attrib2': 3, 'attrib': 2}
After {'attrib2': 3}
Here I have an attribute 'a', which is defined in first class method and should be changed in second.
When calling them in order, this message appears:
AttributeError: 'Class' object has no attribute 'a'
The only way I've found - define 'a' again in second method, but in real code it has long inheritance and app will be messed.
Why doesn't it work? Isn't self.a equal to Class.a?
class Class(object):
def method_1(self):
self.a = 1
def method_2(self):
self.a += 1
Class().method_1()
Class().method_2()
Short answer, no. The problem with your code is that each time you create a new instance.
Edit: As abarnert mentions below, there is a big difference between Class.a and c.a. Instance attributes (the second case) belong to each specific object, whereas class attributes belong to the class. Look at abarnert's comment below or the discussion here for more info.
Your code is equivalent to
c1 = Class()
c1.method_1() # defines c1.a (an instance attribute)
c2 = Class()
c2.method_2() # c2.a undefined (the c2 instance doesn't have the attribute)
You probably want to do somthing like
c = Class()
c.method_1() # c.a = 1
c.method_2() # c.a = 2
print "c.a is %d" % c.a # prints "c.a is 2"
Or probably even better would be to initialize c with an a attribute
class Class:
def __init__(self):
self.a = 1 # all instances will have their own a attribute
A newly-created instance of Class has no attribute a when you do instance_of_class.method_2() without calling method_1, as in your example.
Consider this slightly altered version of your code:
class CreateNewClassInstance(object):
def create_a(self):
self.a = 1
def add_one_to_a(self):
self.a += 1
CreateNewClassInstance().create_a()
CreateNewClassInstance().add_one_to_a()
Each time you call Class() (or CreateNewClassInstance()) you create a new object, with its own attribute a. Until you initialize a, you don't have an attribute with that name.
Most of the time this isn't an issue - however, += will attempt to load self.a before adding one to it - which is what is causing your AttributeError in this case.
It seems that in Python, to declare a variable in a class, it is static (keeps its value in the next instances). What better way to get around this problem?
class Foo():
number = 0
def set(self):
self.number = 1
>>> foo = Foo()
>>> foo.number
0
>>> foo.set()
>>> foo.number
1
>>> new_foo = Foo()
>>> new_foo.number
1
Variables defined at the class level are indeed "static", but I don't think they work quite the way you think they do. There are 2 levels here which you need to worry about. There are attributes at the class level, and there are attributes at the instance level. Whenever you do self.attribute = ... inside a method, you're setting an attribute at the instance level. Whenever python looks up an attribute, it first looks at the instance level, if it doesn't find the attribute, it looks at the class level.
This can be a little confusing (especially if the attribute is a reference to a mutable object). consider:
class Foo(object):
attr = [] #class level attribute is Mutable
def __init__(self):
# in the next line, self.attr references the class level attribute since
# there is no instance level attribute (yet)
self.attr.append('Hello')
self.attr = []
# Now, we've created an instance level attribute, so further appends will
# append to the instance level attribute, not the class level attribute.
self.attr.append('World')
a = Foo()
print (a.attr) #['World']
print (Foo.attr) #['Hello']
b = Foo()
print (b.attr) #['World']
print (Foo.attr) #['Hello', 'Hello']
As others have mentioned, if you want an attribute to be specific to an instance, just initialize it as an instance attribute in __init__ (using self.attr = ...). __init__ is a special method which is run whenever a class is initialized (with a few exceptions that we won't discuss here).
e.g.
class Foo(object):
def __init__(self):
self.attr = 0
Just leave the declaration out. If you want to provide default values for the variables, initialize them in the __init__ method instead.
class Foo(object):
def __init__(self):
self.number = 0
def set(self):
self.number = 1
>>> foo = Foo()
>>> foo.number
0
>>> foo.set()
>>> foo.number
1
>>> new_foo = Foo()
>>> new_foo.number
0
Edit: replaced last line of the above snippet; it used to read 1 although it was just a typo on my side. Seems like it has caused quite a bit of confusion while I was away.
You maybe want to change the class attribute:
class Foo():
number = 0
def set(self):
Foo.number = 1
instead of overriding it!