Yes, though c.__class__.a or type(c).a. The two differ slightly in that old-style classes (hopefully, those are all dead by now - but you never know...) have a type() of <type 'instance'> (and __class__ works as expected) while for new-style classes, type() is identical to __class__ except when the object overrides attribute access.
Can someone explain how object/instance variables vs class/static variables work in Python?
python - Object vs. class variable - Stack Overflow
Python Class Variables Vs Instance Variables - Stack Overflow
When to use class variables vs instance variables
So I come from a Java background where defining, declaring and accessing static and instance level variables are pretty much a straightforward process. I want to be able to understand OOP concepts of Python properly so I have been doing some practice.
I have a class:
class A:
def init(self): pass
def someFunc(self): self.var1 += 1
I create an object of this class and call the someFunc() method:
a = A() a.someFunc()
It gives me an error. Ok, fair enough since I haven't declared a self.var1 variable yet.
Consider another example.
class A:
var1 = 10
def init(self): pass
def someFunc(self): self.var1 += 1
Now when I do this:
a = A() a.someFunc() Output: 11
I know that variables defined just below the class definition are class/static variables. And to access them you have to do A.var1
But why does it not give me an error now? I haven't created a object/instance level self.var1 variable yet, just a class level variable var1.
And when I call A.var1 the output is 10. Why is the output not the same as a.var1?
Does python automatically use the class level variable with the same name since there is no instance level variable defined with the same name? And does that in turn become a different variable from the class level variable?
Can someone please elaborate?
Yes, though c.__class__.a or type(c).a. The two differ slightly in that old-style classes (hopefully, those are all dead by now - but you never know...) have a type() of <type 'instance'> (and __class__ works as expected) while for new-style classes, type() is identical to __class__ except when the object overrides attribute access.
After you assign to it on the class instance, there is both a class attribute named a and an instance attribute named a. I illustrate:
>>> class Foo(object):
... a = 10
...
>>> c = Foo()
>>> c.a
10
>>> c.a = 100 # this doesn't have to be done in a method
>>> c.a # a is now an instance attribute
100
>>> Foo.a # that is shadowing the class attribute
10
>>> del c.a # get rid of the instance attribute
>>> c.a # and you can see the class attribute again
10
>>>
The difference is that one exists as an entry in Foo.__dict__ and the other exists as an entry in c.__dict__. When you access instance.attribute, instance.__dict__['attribute'] is returned if it exists and if not then type(instance).__dict__['attribute'] is checked. Then the superclasses of the class are checked but that gets slightly more complicated.
But at any rate, the main point is that it doesn't have to be one or the other. A class and an instance can both have distinct attributes with identical names because they are stored in two separate dicts.
Defining the variables like you did makes them class variables. If you want to guarantee a fresh copy for every instance, you'd do something like
class Animal:
def __init__(self):
self.x = {}
self.y = 0
In your case the definition
class Animal:
x = {}
y = 0
means that every instance of the class Animal gets the identical reference to an empty dictionary, there are not multiple dictionaries. If you add to that dictionary, every instance will know. If you wanted to give an instance it's own version, you could technically do animal1.x = {'num': 14}. This way you create a new dictionary and assign it. This won't affect animal2.
Class variables can be difficult to understand at first. They are essentially variables that are shared between all instances of a class. Two practical applications of this is are as an instance counter, or as default settings for all objects. If they are changed through the definition itself (Animal.y = 14) they will be updated for all instances and future instances.
This contrasts entirely with instance variables which are always specific to and only accessible through their respective object.
However, the problem with the example you provided is that you are confusing assignment with mutation. You mutated the dictionary whereas you assigned the integer. To assign the dictionary the same way you assigned 14 to the y variable you would have to create a new dictionary like so:
animal1.x = {'num': 14}
This way it will not affect the animal2 object.
I thought I understood the use cases for class vs instance variables:
-
Class: when you want the variable to be the same across all objects
-
Instance: when you want the variable to be unique to each object
But I'm working through the Codecademy DSA with Python course, and doing an implementation of a hash map. The hashing function (.hash()) finds the byte-code version of the key string, and sums it for the hash. Why is Codecademy's solution of key_bytes using a class variable, vs I thought an instance variable is more appropriate? Because for each hash map I make, I will want a unique key_byte per object?
class HashMap:
def __init__(self, array_size):
self.array_size = array_size
self.array = [None for item in range(array_size)]
def hash(self, key):
#codecademy forces this `key_bytes` as a class variable as solution
key_bytes = key.encode()
self.hash_code = sum(key_bytes)
#why is `key_bytes` not an instance variable:
self.key_bytes = key.encode()
self.hash_code = sum(self.key_bytes)