DEV Community

Cover image for Confused by Python self scope
Tib
Tib

Posted on

Confused by Python self scope

(picture from Dex Ezekiel)

Today, I was playing with risky naming conflicting (that I generally avoid) and I get confused by a class method/attribute resolution in python.

Specifically I was playing with classes and code samples like the following:

# arg arg arg
class arg():
    def arg(self, arg=None):
        self.arg = arg
        print("arg")

arg = arg()
arg.arg()
Enter fullscreen mode Exit fullscreen mode

And I was confused by the scope of self:

class parent():
    def __init__(self):
        self.f() # Boom
    def f(self):
        return

class child(parent):
    def __init__(self):
        self.f = "not a method"
        super().__init__()

child() # Boom (see above)
Enter fullscreen mode Exit fullscreen mode

I intended that self.f() in parent class would call the parent function but it first resolved to child attribute and produced and error:

Traceback (most recent call last):
  File "module.py", line 17, in <module>
    child()
  File "module.py", line 14, in __init__
    super().__init__("tib")
  File "module.py", line 6, in __init__
    self.f()
TypeError: 'str' object is not callable
Enter fullscreen mode Exit fullscreen mode

It's not that strange when you think about it (to get the most "specialized" or "downward" method/attribute) but how do you restrict to the parent class scope with just no method resolution?

Do you know dear fellow python developers?

I don't need it, but I'm curious šŸ˜ƒ

If yes, please comment! šŸ˜ƒ

Top comments (4)

Collapse
 
raigaurav profile image
Gaurav Rai

One thing I noticed while using super is - in pycharm it add parameters automatically but in VS code it doesn't.
Try it like -
super(child, self).__init__()
Though this is 2.x style but I it working in such scenorio.
Better way is to use @abstractmethod or something like - stackoverflow.com/a/57102294/2001559

P.S> I am also new to this world and learning

Collapse
 
thibaultduponchelle profile image
Tib

This super variant is not working for me (python3.7)

Collapse
 
rvodden profile image
Richard Vodden • Edited

So what's going on here is that the f method belongs to the parent class, and is correctly inherited by the child class. The f field, however, belongs to an instance of the child class, not the class itself, and therefore masks the method. I'm not sure what you're actually trying to achieve, as if you'd like to have a string in your child class that doesn't override the parent, its probably best just to give it another name. You can get the result you're expecting by having an instance field holding the method though:

class Parent():
    def __init__(self):
        self.f = self.method

    def method(self):
        print("in a method")

class Child(Parent):
    def __init__(self):
        self.f = "not a method"
        super().__init__()

c = Child()
c.f()
Enter fullscreen mode Exit fullscreen mode
in a method
Enter fullscreen mode Exit fullscreen mode

This is a bit confusing when you start out, as it feels like methods annotated with @classmethod should beling to the class, and those not annotated should belong to the instance. In practice, however, that would mean a copy of every method would have to reside with every instance, which would be terrible wasteful. In fact, the @classmethod annotation just means that the method is called with the class a the first parameter not the instance.

Also just as an aside, there is a style standard for python called PEP8 and that says that classes should have capital letters, it doesn't make your code work any differently though.

I hope that helps!

Collapse
 
thibaultduponchelle profile image
Tib

Thank you! :)