简体   繁体   中英

Why doesn't Python use C++/Java-like syntax to define instance variables?

This plagued me for hours, as I am from the C++ world. I finally found out what was going on, but I do not know why this is the default behaviour. I'd like to understand why the language is designed this way.

I wanted an instance variable mem . So I tried this:

class x(object):
   mem = []

obj = x()
obj.mem.append(1)
print(obj.mem)
objTWO = x()
objTWO.mem.append(2)
print(objTWO.mem)

Which prints this:

[1] 

[1, 2]

Whereas:

class x(object):

    def __init__(self):
        self.mem = []

obj = x()
obj.mem.append(1)
print(obj.mem)
objTWO = x()
objTWO.mem.append(2)
print(objTWO.mem)

prints

[1]

[2]

Why would the first be the default behaviour? What is the intuition here, since its the opposite of how many mainstream OO languages work (they introduce the static keyword for the top case, which makes you explicitly say you want a static variable)? To newcomers to Python, this is a surprise.

Also, it seems you are allowed to have an instance variable and a class variable with the same name:

class x(object):
    mem = []

    def __init__(self):
        self.mem = []

I would have to run this to figure out what would be printed. I can't even guess!

The intuition is that in Python, everything is an object, including classes themselves. There's no such thing as a "static" keyword in Python; there are classes, which are objects, and those classes have attributes. Everything that appears in the class definition is a class attribute -- that includes both methods and other kinds of attributes.

The benefit of this design is simplicity and consistency. There's no distinction between public or private, or between static and non-static attributes. The distinction between class and instance attributes emerges naturally through the class constructor. When __init__ is called (indirectly via ClassName(params) ), it receives a new instance of the class via the self parameter, and it then modifies that instance directly. Everything happens explicitly, through constructs that are already defined -- you don't have to understand any new syntax or new keywords to see what's happening when an instance is created.

Then all you have to understand is Python's model for attribute lookup. It works almost identically to PATH resolution in most shells. In bash, for example, when you execute a command ( ls ), the first entry in the path is searched, then the second and so on, and the first version of the command that's found is the one that's executed. But in this case, the path looks like this (at least in simple cases with single inheritance):

instance; class; superclass; superclass; ... and so on

This really isn't so different from name resolution in nested scopes in c or c++. You complain:

Even worse is that it seems you are allowed to have an instance variable and a class variable with the same name.

But is that really any more confusing than the fact that c allows you to define int i; in a block, create an inner block, and then define another int i; that masks the original?

I agree that it's counter-intuitive, but it's got a good use -- aliasing functions.

This is from a library I wrote a while back, edited down for length

...
class UndrawnTurtle():
    """Acts just like the turtle package, but without bringing up a window to show it."""
...

    def forward(self, distance):
        angle_radians = math.radians(self.angle)

        self.x = self.x + math.cos(angle_radians) * distance
        self.y = self.y + math.sin(angle_radians) * distance

        self._visit()

...
    # Now for some aliases. Everything that's implemented in this class
    # should be aliased the same way as the actual api.
    fd = forward
...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM