How to access a parent instance variable using super() - python

I was playing around a bit with polymorphism and super() in python classes with inheritance and found that I didn't understand the following behavior.
In this dummy example, I found that I can access the instance variables and class variables for the child class, and class variable for the parent class, directly from the object of the child class, BUT I was unsuccessful in accessing the instance variable for the parent class.
I understand that I would have to create an object of the parent class using super() but am unable to access the instance variables from parent class. I don't want to explicitly return anything in the parent.__init__().
The only way I found to do this is to explicitly create an object of the parent class and then fetch the required variable, but that hinders proper abstraction of the code.
Is there a way I can access parent_object._color after its constructor is run, using super(), without explicit instantiation of the parent class object?
#parent class
class hat:
_color = 'red' #Parent class variable
def __init__(self):
self._color = 'yellow' #Parent object variable
#child class
class cap(hat):
_color = 'green' #Child class variable
def __init__(self, color): #Child instance variable (passed)
self._color = color
#property
def parent_color_super(self):
return super()._color #super() only gives access to class variables
#property
def parent_color_explicit(self):
return hat()._color #explicit object constructor allows access to instance variable
c = cap('blue')
print(c._color) #Attribute lookup: Instance variable
print(c.__class__._color) #Attribute lookup: class variable
print(c.__class__.__bases__[0]._color) #Attribute lookup: parent class variable
print(c.parent_color_super) #<---
print(c.parent_color_explicit) #<---
blue
green
red
red #<--- how to get this as yellow!
yellow #<---
EDIT 2:
Based on the valuable answers from #Silvio Mayolo, #user2357112, and #Mad Physicist, I think I understand where the issue was.
As I understand, the child class's instance c will hold the instance variable _color which will get overwritten by the super().__init__() as the job of that function is to update the self._color of whichever object its called from.
This means, as clarified by the answers, there is no separate "parent instance variables" that can be accessed by super(); only class attributes such as the class variable _color (red) and __init__() (which sets the instance variable _color to (yellow).
A modified example to show this behavior is as follows -
class hat:
_color = 'red' #Parent class variable
def __init__(self): #Sets instance variable to yellow
self._color = 'yellow'
class cap(hat):
_color = 'green' #Child class variable
def __init__(self, color): #Child instance variable (passed)
self._color = color
def parent_color_super(self): #Call __init__ from parent, overwrites _color
super().__init__()
c = cap('blue')
print(c._color) #Attribute lookup: Instance variable
print(c.__class__._color) #Attribute lookup: class variable
print(c.__class__.__bases__[0]._color) #Attribute lookup: parent class variable
c.parent_color_super() #Call init from super class
print(c._color) #Attribute lookup: Updated Instance variable
blue
green
red
yellow

There is no separate set of parent class instance variables. The instance variable you are trying to access does not exist. The only instance variable your c has is a single _color instance variable, set to 'blue'.
Your parent_color_explicit does not access what you're trying to access. It creates an entirely separate, completely unrelated instance of hat, and accesses that instance's instance variables.
Heck, you never even called super().__init__ in cap.__init__, so the self._color = 'yellow' assignment never happens at all for c. (It wouldn't solve your problem if you did call super().__init__, because self._color can't be both 'yellow' and 'blue'. Only one of those values would be saved, depending on which assignment happens second.)

Instance variables and methods are fundamentally different beasts. A method, generally speaking, is defined on a class. It happens to be called on an instance, but it doesn't, in any reasonable sense, exist on that instance. More concretely
class Hat:
_color = 'red'
def __init__(self, color):
self._color = color
def example(self):
print("I'm a Hat")
class Cap(Hat):
_color = 'green'
def __init__(self, color):
self._color = color
def example(self):
print("I'm a Cap")
my_hat = Hat('yellow')
my_cap = Cap('orange')
Let's be perfectly clear about what's happening here. Hat is a class which has three slots defined on it: _color, __init__, and example. Cap is also a class (a subclass of Hat) which has three slots defined on it: _color, __init__, and example. In each case, _color is a string and the other two are functions. Ordinary Python functions at this point, not instance methods. The instance method magic comes later using __get__.
Now, my_hat is a hat. To construct it, we allocated some memory for a new object called my_hat and then called Hat.__init__(my_hat, 'yellow'). This set the _color field on my_hat (which is unrelated to the _color field on Hat at this point). If we were to, at this point, call my_hat.example(), that would end up calling Hat.example(my_hat), through the __get__ magic method on function. Note carefully: At no point is example actually defined on my_hat. It's defined on the class Hat and then the rules of Python allow us to access it as my_hat.example().
Now, with my_cap, we allocate some memory and call Cap.__init__(my_cap, 'orange'). This initializes the instance variable _color to 'orange'. In your current code, we never call the superclass constructor, but even if we did do super().__init__('purple'), the result would get overwritten. The object my_cap can only have _color defined on it once. There's no virtual inheritance for that: it's literally defined on the object, in the exact same way a key is given a value in a dictionary.
The reason super() works for methods is that the parent method still exists on the parent class. Methods aren't called on objects, they're called on classes and happen to take an instance of that class as the first argument. So when we want to access a parent class method, we can simply do Hat.example(my_cap). The super() syntax just automates this process. By contrast, in the case of instance variables that are literally defined on the object, not on a class, the old value is gone as soon as you overwrite it. It never existed at that point. The fact that there are class-level variables with the same name is irrelevant, those serve no purpose to an instance which has already defined a field with the same name.
If you want virtual inheritance, you need names to be defined on the class. So you need functions, or if you want the property syntax, you need #property. At some point, you're going to have to access a (real, concrete, non-virtual) backing field, but you can hide that behind #property if you like.
class Hat:
def __init__(self):
self._Hat_color = 'yellow'
class Cap(Hat):
def __init__(self, color):
super().__init__()
self._Cap_color = color
#property
def hat_color(self):
return self._Hat_color
my_hat = Hat('yellow')
my_cap = Cap('orange')
If you want to automate the process of mangling instance variable names from child classes even further, you can use dunder variables. self.__color will get renamed to self._Hat__color internally if used inside of Hat. It's just a trick of naming, not any deeper magic, but it can help with avoiding conflicts.

You are missing the point of how method inheritance works. Since your child class defines an __init__ method that does not call super().__init__, the line self._color = 'yellow' will never be called.
There are a couple of things you can do. One option is to call super().__init__() somehow:
def __init__(self, color):
self._color = color
super().__init__()
Now of course you lose the input variable color since it gets overwritten by 'yellow' no matter what.
Another alternative is to call super().__init__() on the instance. __init__ is just a regular method as far as you are concerned, so you don't need to call an allocator to call it:
#property
def parent_color_explicit(self):
color = self._color
super().__init__()
color, self._color = self._color, color
return color
The point is that an instance contains one __dict__. That dictionary can contain one single instance of the _color key. You can access the __dict__ of parent classes via super(), but for the most part that will give you access to a bunch of alternative method implementations. Those implementations still operate on the one and only instance dictionary.
If you want to have multiple definitions of a thing, name it something else in instances of the child class.

Related

Why not use self.varibale_name while calling instance attribute [duplicate]

This question already has answers here:
What is the purpose of the `self` parameter? Why is it needed?
(26 answers)
Closed 4 years ago.
when we create a class which is having instance attribute,why don't we call the instance attribute using self keyword.Please
class car:
def __init__(self, type, color):
self.type = type
self.color = color
c1 = car('SUV','Red')
print(c1.type)
Why not print(c1.self.type), because self.type is the actual attribute.
Got the following error:
AttributeError: 'car' object has no attribute 'self'
When you're describing methods (__init__ in your case), self is refers to instance, that will be passed in future — you need this, because you don't know what instances will be created from your class after.
When you're building instance with c1 = car(...):
at first, python makes "empty" object and assigns it to c1
then this empty object being passed to initializer, roughly as car.__init__(c1, 'SUV', 'Red')
And your __init__ actually mutates c1, just by referencing to that object as self.
Here's example that may help you better understand, step by step.
Python objects is not restricted by any schema of fields (by default). This means that you can add or remove attributes in runtime – after instance been created:
class Car:
pass #does nothing
car = Car()
car.color = 'red'
car.type = 'SUV'
You have class that does not describe anything, except the name, you can add type/color/whatever you want after object creation.
The downside of this – there's a huge room to make mistake, plus you need to write a lot of code as your data model grows.
The next logical step to take - make a "mutation" function, that takes object + required attribute values, and bundle all mutation logic in one place:
class Car:
pass
def prepare_car(car_instance, color, type):
car_instance.color = color
car_instance.type = type
car = SimpleCar()
prepare_car(color='red', type='SUV')
Now all mutation logic is in one place, any user can call the same call, no need to re-write all mutation code every time. Logically, code that mutates car, is highly related to SimpleCar class (well, because it changes car color and type). It would be good to have it bound to class somehow. Let's move it to class namespace:
class Car:
def prepare_car(car_instance, color, type):
car_instance.color = color
car_instance.type = type
we've just moved our function in scope of object. What does that mean for us? To access that function, we need to provide "namespace" first, i.e. Car.prepare_car – reads as "take a class Car_, look for prepare_car method". Now we can call it with
car = Car()
Car.prepare_car(car, color='red', type='SUV')
Here come the magic of python OOP. Once you have instance of a class, you can call methods on instance itself, not from class scope. And what happens when you do so – python will automatically pass instance itself as first argument to function:
car = Car()
Car.prepare_car(car, color='red', type='SUV') # calling class.function
is the same as
car = Car()
car.prepare_car(color='red', type='SUV') # calling instance.method
This is the reason why first argument for methods usually called self this is only convention, but it reads good, consider re-factored code
class Car:
def prepare(self, color, type):
self.color = color
self.type = type
car = Car()
car.prepare(color='red', type='SUV')
now method is called prepare, not prepare_car, because we know what we'll mutate – this is in scope of Car class, so we expect this function to interact with Cars. First argument now called self, because we'll mostly call it on instances (like my_car.prepare(...)), so instance changes itself.
And finally, the __init__. This method (if provided) will be automatically called on instance after it's been created, and signature for instantiating class changes accordingly. Lets make prepare method and initiation part:
class Car:
def __init__(self, color, type):
self.color = color
self.type = type
car = Car(color='red', type='SUV')

Python inheritance: when and why __init__

I'm a Python newbie, trying to understand the philosophy/logic behind the inheritance methods. Questions ultimately regards why and when one has to use the __init__ method in a subclass. Example:
It seems a subclass inheriting from a superclass need not have its own constructor (__init__) method. Below, a dog inherits the attributes (name, age) and methods (makenoise) of a mammal. You can even add a method (do_a_trick) Everything works as it ``should", it seems.
However, if I wanted to add a new attribute in the subclass as I attempt to do in the Cats class, I get an error saying "self" is not defined. Yet I used "self" in the definition of the dog class. What's the nature of the difference?
It seems to define Cats as I wish I need to use __init__(self,name) and super()__init__(name). Why the difference?
class Mammals(object):
def __init__(self,name):
self.name = name
print("I am a new-born "+ self.name)
self.age = 0
def makenoise(self):
print(self.name + " says Hello")
class Dogs(Mammals):
def do_a_trick(self):
print(self.name + " can roll over")
class Cats(Mammals):
self.furry = "True" #results in error `self' is not defined
mymammal = Mammals("zebra") #output "I am a new-born zebra"
mymammal.makenoise() #output "zebra says hello"
print(mymmmal.age) #output 0
mydog = Dogs("family pet") #output "I am a new-born family pet"
mydog.makenoise() #output "family pet says hello"
print(mydog.age) # output 0
mydog.do_a_trick() #output "family pet can roll over"
Explicit is better than implicit.
However, you can do below:
class Dogs(Mammals):
def __init__(self):
#add new attribute
self.someattribute = 'value'
Mammals.__init__(self)
or
class Dogs(Mammals):
def __init__(self):
#add new attribute
self.someattribute = 'value'
super(Mammals, self).__init__()
if I wanted to add a new attribute in the subclass as I attempt to do
in the Cats class, I get an error saying "self" is not defined. Yet I
used "self" in the definition of the dog class.
In your superclass, Mammal, you have an __init__ function, which takes an argument that you've chosen* to call self. This argument is in scope when you're in the body of the __init__ function - it's a local variable like any local variable, and it's not possible to refer to it after its containing function terminates.
The function defined on the Dog class, do_a_trick, also takes an argument called self, and it is also local to that function.
What makes these variables special is not their name (you could call them anything you wanted) but the fact that, as the first arguments to instance methods in python, they get a reference to the object on which they're called as their value. (Read that last sentence again a few times, it's the key to understanding this, and you probably won't get it the first time.)
Now, in Cat, you have a line of code which is not in a function at all. Nothing is in scope at this point, including self, which is why this fails. If you were to define a function in Cat that took an argument called self, it would be possible to refer to that argument. If that argument happened to be the first argument to an instance method on Cat, then it would have the value of the instance of Cat on which it had been called. Otherwise, it would have whatever got passed to it.
*you have chosen wisely!
Declarations at the top level of a Python class become class attributes. If you come from a C++ or Java background, this is similar to declaring a static member variable. You cannot assign instance attributes at that level.
The variable self usually refers to a specific instance of a class, the one from which the method has been called. When a method call is made using the syntax inst.method(), the first argument to the function is the object inst on which the method was called. In your case, and usually by convention, that argument is named self within the function body of methods. You can think of self as only being a valid identifier within method bodies then. Your assignment self.furry = True does not take place in a method, so self isn't defined there.
You have basically two options for achieving what you want. The first is to properly define furry as an attribute of the cat class:
class Cat(Mammals):
furry = True
# Rest of Cat implementation ...
or you can set the value of an instance variable furry in the cat constructor:
class Cat(Mammals):
def __init__(self):
super(Mammals, self).__init__(self)
self.furry = True
# Rest of Cat implementation ...
If you're getting into Python I highly recommend to read these two parts of the Python documentation:
Python classes
Python data model special methods (more advanced)
As pointed out in the other answers, the self that you see in the other
functions is actually a parameter. By Python convention, the first parameter in
an instance method is always self.
The class Cats inherits the __init__ function from its base class,
Mammals. You can override __init__, and you can call or not call the base
class implementation.
In case the Cats __init__ wants to call the base implementation, but doesn't want to care about the parameters, you can use Python variable arguments. This is shown in the following code.
Class declaration:
class Cats(Mammals):
def __init__(self, *args):
super().__init__(*args)
self.furry = "True"
See, for example, this Stack Overflow question for something about the star
notation for variable numbers of arguments:
Can a variable number of arguments be passed to a function?
Additional test code:
cat = Cats("cat")
print(vars(cat))
Output:
I am a new-born cat
{'name': 'cat', 'age': 0, 'furry': 'True'}
You can do something like in Chankey's answer by initiating all the variables in the constructor method ie __init__
However you can also do something like this
class Cats(Mammals):
furry = "True"
And then
cat = Cats("Tom")
cat.furry # Returns "True"
The reason you can't use self outside the functions is because self is used explicitly only for instances of the class. If you used it outside, it would lead to ambiguity. If my answer isn't clear please let me know in comments.
The __init__ method runs once on the creation of an instance of a class. So if you want to set an attribute on an instance when it's created, that's where you do it. self is a special keyword that is passed as the first argument to every method, and it refers to the instance itself. __init__ is no different from other methods in this regard.
"What's the nature of the difference": you define the method Dog.do_a_trick, and you receive self as an argument to the method as usual. But in Cat you've unintentionally (perhaps subconsciously!) attempted to work on the class scope -- this is how you'd set a class attribute whose value is identical for all cats:
class Cat(object):
sound = "meow"
It's different so you can have both options available. Sometimes (not all the time, but once in a while) a class attribute is a useful thing to have. All cats have the same sound. But much of the time you'll work with instance attributes -- different cats have different names; when you need that, use __init__.
Suppose you have a class named Person which has a method named get_name defined as :
class Person():
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
def get_name(self):
return self.first_name + ' ' + self.last_name
And, you create an instance of Person as p1. Now when you call the function get_name() with this instance, it will converts internally
Person.get_name(p1)
So, self is the instance itself.
Without self you can write above code as :
class Person():
first_name = None
last_name = None
def get_name(personobject):
return personobject.first_name + ' ' + personobject.last_name
What I am trying to say is the name self is a convention only.
And for inheritance, if you would like to have extra attributes in your subclass, you need to initiate your super class first and add your parameter as you wanted.
For example, if you want to create a subclass from Person named Boy with new attribute height, the you can define it as:
class Boy(Person):
def __init__(self, first_name, last_name, height):
super(Person, self).__init__(first_name, last_name)
self.height = height

Initializing parent class instance attributes

I understand how to initialize a parent class to get their instance attributes in a child class, but not exactly what's going on behind the scenes to accomplish this. (Note: not using super intentionally here, just to make illustration clear)
Below we extend class A by adding an extra attribute y to the child class B. If you look at the class dict after instantiating b=B(), we rightfully see both b.x(inherited from class A) and b.y.
I assume at a high level this is accomplished by the call to A.__init__(self,x=10) performing something similar to b.x=10 (the way a normal instance attribute would be assigned) within the __init__ of class B. It's a bit unclear to me because you are calling the __init__ of class A, not class B, yet class B still gets it's instance attributes updated accordingly. How does class A's __init__ know to update b's instance attributes.
This is different than inherited methods where the b object has no explicit inherited method in it's particular namespace, but looks up the inheritance chain when a call to a missing method is made. With the attribute, the method is actually in b's namespace (it's instance dict).
class A:
def __init__(self,x):
self.x = x
class B(A):
def __init__(self):
A.__init__(self,x=10)
self.y = 1
b = B()
print(b.__dict__)
>>>{x:10,y:1} #x added to instance dict from parent init
Below we inherit from the built-in list. Here, similar to the above, since we are calling the list's __init__ method within Foolist's __init__, I would expect to see an instance dictionary that contains elems, but it is nowhere to be found. The values 123 are in the object somewhere, as can be seen by printing alist, but not in the instance dict.
class Foolist(list):
def __init__(self, elems):
list.__init__(self, elems)
alist = Foolist('123')
So what exactly is going on in the inheriting class when a parent's __init__ is called from a child's __init__? How are values being bound? It seems different from method lookup, as you are not searching the inheritance chain on demand, but actually assigning values to the inheriting class's instance dict.
How does a call to a parents init fill out it's child's instance dict? Why does the Foolist example not do this?
The answer is simple: self.
As a very rough overview, when instantiating a class, an object is created. This is more or less literally just an empty container without affiliation to anything.* This "empty container" is then passed to the __init__ method of the class that is being instantiated, where it becomes... the self argument! You're then setting an attribute on that object. You're then calling a different class's __init__ method, explicitly passing your specific self object to that method; that method then adds another attribute to the object.
This is in fact how every instance method works. Each method implicitly receives the "current object" as its first argument, self. When calling a parent's __init__ method, you're actually making that object passing very explicit.
You can approximate that behaviour with this simple example:
def init_a(obj):
obj.x = 10
def init_b(obj):
init_a(obj)
obj.y = 20
o = {}
init_b(o)
* The object is not entirely "empty", there are particular attributes set on the object which create an affiliation with a particular class, so the object is "an instance of" a certain class, and Python can locate all the methods it so inherits from the class as needed.

Why it's not possible to create object attribute outside object methods?

While researching about python class attribute and instance attribute, I came to know that it's not possible to create object attribute outside object methods (or may be class method). Like code below will generate an "NameError" in python.
class test(object):
def __init__(self):
self.lst = []
self.str = 'xyz'
Why python doesn't allow this? I'm not questioning language creator's decision, but any reason behind this. Like, is it technically incorrect or any other disadvantage of this behavior.
You are defining a class, so there is no instance to point to outside methods. Drop the `self:
class test(object):
def __init__(self):
self.lst = []
str = 'xyz'
self points to the instance, not the class. You either need to create an instance and assign directly to attributes (test().str = 'xyz') or you need to be inside a method (when self can actually refer to an instance).
self is not a special name in python, you could use \
class test(object):
def __init__(foo):
foo.lst = []
If you want. Every method of a class gets the instance explicitly passed to it as the first parameter, you can call it whatever you want. Trying to access a parameter outside the scope of the method obviously won't work.

A private list variable is inadvertently shared between instance objects

I created many instances of a PlotHandler class. An instance must keep it's variables private. But the way I managed them led to a hard to detect problem, a private list variable is shared between instances! And that too without any obvious source for the leak.
My debugging told me that the private member function that modifies the list sees the same list, even if they are different objects.
Is this a "gotcha" problem? What is the best way to troubleshoot this?
Here are the relevant parts (I hope they are!) of the implementation. Please see the ALL-CAPS comments:
The file implementing PlotHandler:
class PlotHandler(wx.Frame):
__crop_section = None
__projection = None
__crop_xcord = None
_band_data = [] #THIS GETS SHARED
def _on_plot_click(self, xcord): #CALLED BY ANOTHER OBJECT
band = self._analyze_band( xcord )
self._band_data.append(band)
...
The parent class that it is managing PlotHandlers:
class MainFrame(wx.Frame):
__close_callback__ = None
_plot_handlers = []
def __init__(self, parent, title):
...
def InitUI(self):
...
img_handler = ImageHandler(panel)
self.img_src.register_callback( img_handler.update_image )
#you need to call PlotHandler(parent, cropped)
img_handler.register_sample_callback( self._create_new_plot_handler )
...
def _create_new_plot_handler(self, cropped_sample ):
self._plot_handlers.append( PlotHandler(self, cropped_sample) ) #CREATE THEM
See this question, this one, and tons of other stuff you can find by googling "Python class variables shared", "Python FAQ class variables", etc.
The short answer is: variables defined directly in the class body are class variables, not instance variables, and are thus shared among instances of the class. If you want instance variables you must assign them from within a method, where you have access to self.
Class attributes are shared between instances. If you want to define an instance attribute (so each object have its own reference to the variable) you have to define it in __init__
class PlotHandler(wx.Frame):
__crop_section = None
__projection = None
__crop_xcord = None
def __init__(self, **kwargs):
self._band_data = [] #THIS IS NOT SHARED

Categories