Укажите атрибуты в конструкторе в Python

Меня смущают различия следующих кодов:

class car:
    def __init__(self, weight):
        self.weight = weight

class car:
    def __init__(self, weight):
        self.weight = 0

class car:
    def __init__(self, weight=0):
        self.weight = weight

class car:
    def __init__(self, weight=0):
        self.weight = 0

class car:
    def __init__(self, weight=0):
        self.weight = 1

class car:
    def __init__(self):
        self.weight = 0

Насколько я понимаю, если я укажу weight=0 в def __init__(self, weight=0), то атрибуты по умолчанию будут равны 0. Но кажется, что я также могу сделать то же самое, если не укажу def __init__(self, weight), а добавлю self.weight = 0 потом. Какая разница? Что, если мы дадим два разных значения?

🤔 А знаете ли вы, что...
Python подходит для начинающих программистов благодаря своей простоте и читаемости кода.


2
50
2

Ответы:

Решено

Вы запутались в понятиях функции arg, значения по умолчанию для arg и переменной-члена, вы просто слишком их смешиваете.

Аргумент функции

Для def __init__(self,weight) вы ожидаете, что при вызове конструктора будет передана переменная, которой присвоено имя «вес». Это аргумент функции.

Значение по умолчанию для аргумента функции

Для def __init__(self,weight=0) вы все еще ожидаете передачи переменной, но все в порядке, если она отсутствует, и в этом случае значение по умолчанию 0 будет просто присвоено символу «вес». Это значение по умолчанию для аргумента функции.

Атрибут участника

Теперь о коде ниже:

class car:
def __init__(self, weight=0):
    self.weight = weight

Фактически это означает: передайте мне переменную, я присвою ее символу «вес», в противном случае я присвою этому символу 0. И я присвою значение символа «вес» «self.weight», они находятся в совершенно разных областях и, следовательно, являются разными символами. Это не похоже на C++, когда вы выполняете почленную инициализацию. «self.weight» — атрибут экземпляров класса.

вес против собственного веса

Будем надеяться, что теперь, приведя последний пример, логика будет достаточно ясна:

class car:
    def __init__(self, weight=0):
        self.weight = 1

Здесь вы получаете символ с именем «вес», независимо от того, по умолчанию он или передан, он просто игнорируется, т. е. никуда не ссылается, вместо этого вы присваиваете 1 переменной-члену self.weight.


1

class car:
    def __init__(self, weight):
        self.weight = weight
  • При инициализации объекта с использованием этого класса требуется аргумент weight (т. е. нельзя вызвать только my_car = car()). И какое бы значение weight ни было передано при создании объекта, оно будет присвоено его атрибуту weight:
>>> my_car = car()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: car.__init__() missing 1 required positional argument: 'weight'

>>> my_car = car(weight=69)
>>> print(my_car.weight)
69

2

class car:
    def __init__(self, weight):
        self.weight = 0
  • При использовании этого класса для создания объекта снова требуется аргумент weight. Но независимо от того, какое значение weight передается при инициализации объекта, оно будет 0:
>>> my_car = car()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: car.__init__() missing 1 required positional argument: 'weight'

>>> my_car = car(weight=69)
>>> print(my_car.weight)
0
  • Однако позже можно изменить/присвоить новое значение атрибуту веса:
>>> my_car.weight = 69
>>> print(my_car.weight)
69

3

class car:
    def __init__(self, weight=0):
        self.weight = weight
  • При создании объекта с использованием этого класса на этот раз weight является необязательным аргументом (т. е. передавать не нужно, но можно передать). Если weight не передан в качестве аргумента, ему будет присвоено значение по умолчанию, в данном случае 0:
>>> my_car = car()
>>> print(my_car.weight)
0
>>> my_car = car(weight=69)
>>> print(my_car.weight)
69

4

class car:
    def __init__(self, weight=0):
        self.weight = 0
  • Хорошо, это интересно (nvm). Опять же, при создании объекта с использованием этого класса аргумент weight не является обязательным. Но что интересно в этом; независимо от того, какое значение weight передается в качестве аргумента или каково его значение по умолчанию, оно всегда будет 0 при создании объекта:
>>> my_car = car() # No errors..
>>> print(my_car.weight)
0
>>> my_car = car(weight=69) # Useless argument..
>>> print(my_car.weight)
0

5

class car:
    def __init__(self, weight=0):
        self.weight = 1
  • Как и 4 выше, weight является необязательным, и независимо от того, какое значение по умолчанию имеет weight или что передается в качестве аргумента при создании объекта, weight будет присвоено значение 1:
>>> my_car = car()
>>> print(my_car.weight)
1
>>> my_car = car(weight=69)
>>> print(my_car.weight)
1

6

class car:
    def __init__(self):
        self.weight = 0
  • Этот класс не принимает никаких аргументов при создании объекта, и если вы попытаетесь передать аргумент, вы получите ошибку. А при создании объекта его атрибуту weight будет присвоено значение 0:
>>> my_car = car(69)
# That positional argument is `self`... 🙂
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: car.__init__() takes 1 positional argument but 2 were given

>>> my_car = car(weight=69)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: car.__init__() got an unexpected keyword argument 'weight'

>>> my_car = car()
>>> print(my_car.weight)
0

Хорошо, так какой из них хорош или какой вам следует использовать? На мой взгляд (вы можете не согласиться), третий хорош/лучше(?), но с небольшими изменениями:

class car:
    def __init__(self, weight):
        self.weight = weight

ПОЧЕМУ?

  1. При создании объекта вам необходимо передать weight в качестве аргумента, иначе выдаст ошибку.
  2. Думаю, ни одна машина в этом мире не будет иметь "веса" 0..
  3. Все машины должны иметь вес, если только они не сделаны из воздуха. <3

Кроме того, следуйте «Соглашению CapWords» для названий классов. 🙂


🫡