DEV Community

Cover image for Confused by Python self scope

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

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):

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

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 "", line 17, in <module>
  File "", line 14, in __init__
  File "", line 6, in __init__
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)

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 -

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

thibaultduponchelle profile image

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

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"

c = Child()
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!

thibaultduponchelle profile image

Thank you! :)