This Stack Overflow answer states that for the program:
class Parent(object):
i = 5;
def __init__(self):
self.i = 5
def doStuff(self):
print(self.i)
class Child(Parent, object):
def __init__(self):
super(Child, self).__init__()
self.i = 7
class Main():
def main(self):
m = Child()
print(m.i) #print 7
m.doStuff() #print 7
m = Main()
m.main()
Output will be:
$ python Main.py
7
7
That answer then compares it to a similar program in Java:
The reason is because Java's int i declaration in Child class makes the i become class scope variable, while no such variable shadowing in Python subclassing. If you remove int i in Child class of Java, it will print 7 and 7 too.
What does variable shadowing mean in this case?
What does variable shadowing mean in this case?
Variable shadowing means the same thing in all cases, independent of context. It's defined as when a variable "hides" another variable with the same name. So, when variable shadowing occurs, there are two or more variables with the same name, and their definitions are dependent on their scope (meaning their values may be different depending upon scope). Quick example:
In [11]: def shadowing():
...: x = 1
...: def inner():
...: x = 2
...: print(x)
...: inner()
...: print(x)
...:
In [12]: shadowing()
2
1
Note that we call inner() first, which assigns x to be 2, and prints 2 as such. But this does not modify the x at the outer scope (i.e. the first x), since the x in inner is shadowing the first x. So, after we call inner(), and the call returns, now the first x is back in scope, and so the last print outputs 1.
In this particular example, the original author you've quoted is saying that shadowing is not occurring (and to be clear: not occurring at the instance level). You'll note that i in the parent takes on the same value as i in the child. If shadowing occurred, they would have different values, like in the example above (i.e. the parent would have a copy of a variable i and the child would have a different copy of a variable also named i). However, they do not. i is 7 in both the parent and child. The original author is noting that Python's inheritance mechanism is different than Java's in this respect.
Variable shadowing occurs when a variable declared within a certain scope (decision block, method, or inner class) has the same name as a variable declared in an outer scope. Then the variable in the scope that you are in shadows (hides/masks) the variable in the outer scope.
In the above code the variable i is being initialized in both the super class and the child class. So the initialization in the super class will be shadowed by the initialization in the child and class.
m = Child() #we initialized the child class with i=7
print(m.i) #eventhough we are calling a method in the super class the value of i in the super class is shadowed by the value we initialized the instance of the child class (m)
m.doStuff() #same thing here
In Java, methods and fields are fundamentally different things, operating by entirely different rules. Only methods are inherited by subclasses; fields are specific to the class that declared them. If a subclass declares a field with the same name as one in a parent class, they are entirely unrelated; methods of the parent class continue to access the parent's version, and methods of the child class access its version. This is what is referred to as shadowing. If the parent class actually wanted to make its field available to children, it would have to define getter/setter methods for it.
In Python, there is no such distinction - methods are basically fields whose value happens to be a function. Furthermore, all of the fields from the entire inheritance hierarchy are stored in a single namespace (typically implemented as a dict attribute named __dict__). If the child and parent use the same name for something, they are necessarily referring to the same object.
Related
Suppose the following class hierarchy in Python:
class O:
variable = 0
class A(O):
variable = "abcdef"
class B(O):
variable = 1.0
class X(A, B):
pass
x = X()
When an instance of X gets created, does Python allocate the memory for each variable in the base classes, or only for the resolved variable?
It doesn't allocate any variables when you create an instance of X; all of your variables are class attributes, not instance attributes, so they're attached to their respective classes, not to the instance. X and instances of X would see variable with the value from A thanks to the order in which it checks base classes for names when the name isn't set on the instance, but all three versions of variable would exist.
If you did make them instance attributes (assigned to self.variable in __init__ for each class), and used super() appropriately to ensure all __init__s called, there'd only be one copy of self.variable when you were done initializing (which one survived would depend on whether you initialized self.variable or called super().__init__() first in the various __init__ implementations). This is because Python doesn't "resolve" names in the way you're thinking; instance attributes are stored by string name on a dict under-the-hood, and the last value assigned to that name wins.
The only way to have multiple instance attributes with the "same" name is to make them private, by prefixing them with __; in that case, with all three defining self.__variable, there'd be a uniquely name-mangled version of __variable seen by the methods of each class (and not used by parent or child classes); X wouldn't see one at all, but methods it inherited from A would see A's version, methods inherited from B would see B's version, etc.
Declaring a variable in a class (outside of a function): all class functions can access it (basically a public variable)
Declaring a variable inside a function inside a class: only that function can access it (it's in that function's scope)
Declaring a variable with self.(variable name) inside a function inside a class: all class functions can access it (how is this different from global (variable name)?)
And since there is no private/protected, everything is public, so everything accessible from inside a class is accessible from outside the class.
Are there any other nuances I should know, or have I pretty much got it?
Since the listing in your question is not 100% clear, I've decided to explain it with a simple example. It also includes some things like __something variables you did not mention in your list.
class Test:
a = None
b = None
def __init__(self, a):
print self.a
self.a = a
self._x = 123
self.__y = 123
b = 'meow'
At the beginning, a and b are only variables defined for the class itself - accessible via Test.a and Test.b and not specific to any instance.
When creating an instance of that class (which results in __init__ being executed):
print self.a doesn't find an instance variable and thus returns the class variable
self.a = a: a new instance variable a is created. This shadows the class variable so self.a will now reference the instance variable; to access the class variable you now have to use Test.a
The assignment to self._x creates a new instance variable. It's considered "not part of the public API" (aka protected) but technically it has no different behaviour.
The assignment to self.__y creates a new instance variable named _Test__y, i.e. its name is mangled so unless you use the mangled name it cannot be accessed from outside the class. This could be used for "private" variables.
The assignment to b creates a local variable. It is not available from anywhere but the __init__ function as it's not saved in the instance, class or global scope.
Declaring a variable at the top level of the class is like declaring a static or class variable. Qualifying it with self is declaring an instance variable. Class variables can be modified by referring to them by class name (e.g. Class.x = 5) and all instances will inherit these changes. Instance variables are private to an instance and can only be modified by that instance.
You can achieve some level of access control using underscores. See private variables in the Python tutorial. By convention, variables starting with one underscore, e.g. _foo are non-public parts of an API, and names starting with two underscores e.g. __foo will have it's name mangled to be _classname__foo.
Although answered, let me add some comments to your questions:
Declaring a variable in a class (outside of a function) : all class functions can access it (basically a public variable)
This is like a static variable and can be called using the class name. These variables are available to all functions, any functions can modify it and print it.
Declaring a variable inside a function inside a class : only that function can access it (it's in that function's scope):
If the variable is declared without self then it is accessible within that function only, like a local variable. However, if it was declared using self like self.var= 'somevalue', then it is accessible via any object but not via the class name.
Declaring a variable with self.(variable name) inside a function inside a class : all class functions can access it (how is this different from global (variable name)?)
See answer in the above part.
And since there is no private / protected, everything is public, so everything accessible from inside a class is accessible from outside the class
Yes, but we can use single underscore to tell the world this variable is private, but technically that actually doesn't make it private.
we can use the scope in this for as :
case 1: In the Class
class test:
def __init__(self, a):
self.__elements = a
def change_a(self): self.__elements = 5
case 2 : Outside class
t = test(5)
This will access by as object._classname__privatevaribalename
print(t._test__elements)
this will print the change value of a
Declaring a variable in a class (outside of a function): all class functions can access it (basically a public variable)
Declaring a variable inside a function inside a class: only that function can access it (it's in that function's scope)
Declaring a variable with self.(variable name) inside a function inside a class: all class functions can access it (how is this different from global (variable name)?)
And since there is no private/protected, everything is public, so everything accessible from inside a class is accessible from outside the class.
Are there any other nuances I should know, or have I pretty much got it?
Since the listing in your question is not 100% clear, I've decided to explain it with a simple example. It also includes some things like __something variables you did not mention in your list.
class Test:
a = None
b = None
def __init__(self, a):
print self.a
self.a = a
self._x = 123
self.__y = 123
b = 'meow'
At the beginning, a and b are only variables defined for the class itself - accessible via Test.a and Test.b and not specific to any instance.
When creating an instance of that class (which results in __init__ being executed):
print self.a doesn't find an instance variable and thus returns the class variable
self.a = a: a new instance variable a is created. This shadows the class variable so self.a will now reference the instance variable; to access the class variable you now have to use Test.a
The assignment to self._x creates a new instance variable. It's considered "not part of the public API" (aka protected) but technically it has no different behaviour.
The assignment to self.__y creates a new instance variable named _Test__y, i.e. its name is mangled so unless you use the mangled name it cannot be accessed from outside the class. This could be used for "private" variables.
The assignment to b creates a local variable. It is not available from anywhere but the __init__ function as it's not saved in the instance, class or global scope.
Declaring a variable at the top level of the class is like declaring a static or class variable. Qualifying it with self is declaring an instance variable. Class variables can be modified by referring to them by class name (e.g. Class.x = 5) and all instances will inherit these changes. Instance variables are private to an instance and can only be modified by that instance.
You can achieve some level of access control using underscores. See private variables in the Python tutorial. By convention, variables starting with one underscore, e.g. _foo are non-public parts of an API, and names starting with two underscores e.g. __foo will have it's name mangled to be _classname__foo.
Although answered, let me add some comments to your questions:
Declaring a variable in a class (outside of a function) : all class functions can access it (basically a public variable)
This is like a static variable and can be called using the class name. These variables are available to all functions, any functions can modify it and print it.
Declaring a variable inside a function inside a class : only that function can access it (it's in that function's scope):
If the variable is declared without self then it is accessible within that function only, like a local variable. However, if it was declared using self like self.var= 'somevalue', then it is accessible via any object but not via the class name.
Declaring a variable with self.(variable name) inside a function inside a class : all class functions can access it (how is this different from global (variable name)?)
See answer in the above part.
And since there is no private / protected, everything is public, so everything accessible from inside a class is accessible from outside the class
Yes, but we can use single underscore to tell the world this variable is private, but technically that actually doesn't make it private.
we can use the scope in this for as :
case 1: In the Class
class test:
def __init__(self, a):
self.__elements = a
def change_a(self): self.__elements = 5
case 2 : Outside class
t = test(5)
This will access by as object._classname__privatevaribalename
print(t._test__elements)
this will print the change value of a
How to access OuterClass variables in a similar manner to how global variables are accessed?
For example:
global_variable = 'global_variable'
class InnerClass:
def __init__(self):
pass
def test(self):
return super().variable
def test_global(self):
return global_variable
class OuterClass:
def __init__(self, inner_class_instance):
self.variable = 'class_variable'
self.inner_class_instance = inner_class_instance
Running the below returns the global variable:
inner = InnerClass()
outer = OuterClass(inner)
outer.inner_class_instance.test_global()
However trying to access the nonlocal class variable results in an AttributeError:
outer.inner_class_instance.test()
super() is incorrectly used in InnerClass.test as OuterClass is not a base class for InnerClass. How to access OuterClass variables in a similar manner to how global variables are accessed? That is without passing a context argument to InnerClass. Using nonlocal variable in InnerClass.test resulted in a SyntaxError.
Also, how and why are the global variable accessible from InnerClass.test?
In the test(self) method of InnerClass, you've wrongly understood the meaning of the expression super().variable. super() here refers to the superclass of InnerClass, which (even though not explicit in the code) happens to be the predefined class called Object. And the Object class certainly doesn't have an attribute called variable. And that is why that line throws the error
'super' object has no attribute 'variable'
Your other question was -- "How to access OuterClass variables?".
I'd like to first clean up some basic aspects of the concepts here.
First of all, that thing you have inside InnerClass, having the name variable, is actually an attribute of InnerClass. So I would rather re-phrase your question as "How to access the OuterClass attributes from within a method of InnerClass?".
Second, just because OuterClass receives a reference to an instance of InnerClass when the __init__() method of OuterClass executes, doesn't mean that OuterClass is in any way "outer to" or "surrounds" InnerClass.
From the point of view of the code in InnerClass, OuterClass is just another class, with no special status -- it is neither a superclass (ancestor class) nor is it an outer class (a surrounding class). Therefore, to access an attribute of an instance of OuterClass, from within the InnerClass method called test(), you first need a name that holds a reference to the OuterClass instance. So, for example, within the test() method, if you happen to have a name called my_outer_inst that holds a reference to an instance of OuterClass, you can certainly refer to the OuterClass attribute called variable, using my_outer_inst.variable.
It's not generally possible for an instance that is assigned as an attribute of some other instance to get a reference to the containing instance. For one thing, there may be zero or more than one such references! For example, if you created a second instance of OuterClass with the same InnerClass instance, there would be two potentially different variable attributes that you might want to read.
In general, if you want to access a object's attributes you need a reference to the object first. It having a reference to you is not enough, unless you arrange some alternative API.
For instance, maybe the InnerClass expects to get a reference to OuterClass in its test method, and OuterClass gets a method to pass itself in:
class InnerClass:
def test(self, outer):
return outer.variable
class OuterClass:
def __init__(self, inner, variable):
self.inner = inner
self.variable = variable
def run_test(self):
return self.inner.test(self)
out = OuterClass(InnerClass(), "foo")
print(out.run_test())
You could also have your classes set up circular references, where they each reference each other. Then either one could do stuff with the other:
class InnerClass:
def __init__(self):
self.outer = None
def test(self):
if self.outer is None:
raise ValueError("Not in an OuterClass instance!")
return self.outer.variable
class OuterClass:
def __init__(self, inner, variable):
self.inner = inner
inner.outer = self # set reference back to us!
self.variable = variable
out = OuterClass(InnerClass(), "foo")
print(out.inner.test())
This is a very crude version of this sort of approach, you might want to ensure your references remained consistent (preventing the same InnerClass instance being used by two different OuterClass instance, for example).
Note that circular references like this make the garbage collector's work harder. Normally Python objects get cleaned up immediately after their last reference goes away. But objects with reference cycles always have references going between each other, and so the GC needs to check if the whole set of objects is dead all together. It will probably manage it pretty well for a cycle that contains just two objects like this example, but a in a larger data structure it might be more tricky.
Starting with your first question, there is something more to define in the code for variable. The AttributeError says ''super' object has no attribute 'variable'' , means it is not defined inside the class.
Try to execute this, inner.test(). You are again getting the same AttributeError. Define/Declare variable and try it once.
Second question, global variables are those that are declared outside the class. They can be accessed anywhere. However, in order to change its value, it has to be declared as global var_name inside the class.
I want to know which one is the best way to access the class variable in a class, either by self, or by class name
I have read somewhere that self is only used for accessing the instance variable. But when I tried with the below code, it is one of the same things. Is is it mean that we can use either of them?
class MyClass:
cls_var = 0 # class variable
def increment(self, incre):
self.cls_var += incre
MyClass.cls_var += incre
def print_var(self):
print(self.cls_var) #Choice 1
print(MyClass.cls_var) # Choice 2
obj1 = MyClass()
obj2 = MyClass()
obj1.increment(5)
obj2.increment(10)
obj1.print_var() #prints 5, 15
obj2.print_var() # prints 15, 15
You should access a class variable only by class name, since that variable is shared among all classes. Thus, to avoid confusion, one should only access class variables by the name of the class; otherwise it might lead to surprising errors (See the second snippet).
In my opinion you should use "Choice 2", as the class variable "cls_var" is shared by all instances. So if you do
ob1.increment(5)
also "ob2.cls_var" gets incremented, but Zen of Python sais that explicit is better than implicit! So do
MyClass.increment(5)
See: https://docs.python.org/2/tutorial/classes.html#class-and-instance-variables.