python class's attribute not in __init__

2019-06-02 21:25发布

问题:

I want to know why the following codes work?

#!/usr/bin/env python3

import sys

class Car():
    def __init__(self):    
        pass

if __name__ == '__main__':
    c = Car()
    c.speed = 3
    c.time = 5
    print(c.speed, c.time)

I accidentally found that I don't have to init attributes in init. I learn from every tutor I have to put assignment in init like below.

#!/usr/bin/env python3

import sys

class Car():
    def __init__(self):    
        self.speed = 3
        self.time = 5

if __name__ == '__main__':
    c = Car()
    print(c.speed, c.time)

If there are some official documents can explain this would be better.

回答1:

It's class attributes vs instance attributes vs dynamic attributes. When you do:

class Car():
    def __init__(self):    
        pass

c = Car()
c.speed = 3
c.time = 5

speed and time are dynamic attributes (not sure if this is an official term). If the usage of the class is such that these attributes are set before calling any other methods of Car, then those methods can use self.speed. Otherwise, you get an error:

>>> d = Car()
>>> d.speed
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Car' object has no attribute 'speed'
>>>

This happens because for c, speed and time are attributes on that instance of Car. Their existence or value doesn't propagate across other instances of Car. So when I create d and then try to lookup d.speed, the attribute doesn't exist. As you've said in your own comment, "they spring into existence when they are first assigned to."

I accidentally found that I don't have to init attributes in init. I learn from every tutor I have to put assignment in init like below.

Your tutors were very wrong or you misunderstood what they meant. In the example you gave, every Car gets the same initial speed and time. Typically, an __init__ would look like this:

class Car():
    def __init__(self, speed, time):  # notice that speed and time are
                                      # passed as arguments to init
        self.speed = speed
        self.time = time

You can then initialise a Car with: c = Car(3, 5). Or put default values in init if it's optional.

Edit: example adapted from the docs:

class Dog:

    kind = 'canine'         # class variable shared by all instances

    def __init__(self, name):
        self.name = name    # instance variable unique to each instance

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.kind                  # shared by all dogs
'canine'
>>> e.kind                  # shared by all dogs
'canine'
>>> d.name                  # unique to d
'Fido'
>>> e.name                  # unique to e
'Buddy'
>>> d.age = 3               # dynamic attribute/variable, unique to d
>>> d.age
3
>>> e.age                   # e doesn't have it at all
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Dog' object has no attribute 'age'