почему я должен использовать @classmethod, если я могу вызвать конструктор без аннотации? - PullRequest
0 голосов
/ 28 мая 2020

Я изучал преимущества @classmethods и решил, что мы можем напрямую вызывать конструктор из любого метода, в таком случае зачем нам метод класса. Есть ли преимущества, которые я упустил?

Почему этот код, каковы преимущества?

class Person: 
    def __init__(self, name, age): 
        self.name = name 
        self.age = age 

    @classmethod
    def fromBirthYear(cls, name, year): 
        return cls(name, date.today().year - year) 

, а не этот код: -

class Person: 
    def __init__(self, name, age): 
        self.name = name 
        self.age = age 

    def fromBirthYear(name, year): 
        return Person(name, date.today().year - year) 

Ответы [ 2 ]

1 голос
/ 28 мая 2020

Учитывая

from datetime import date
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def fromBirthYear(name, year):
        return Person(name, date.today().year - year)

    def __repr__(self):
        return f"Person('{self.name}', {self.age})"

, ваш код работает с поиском, пока вы не получаете доступ к fromBirthYear через экземпляр Person:

>>> Person("bob", 2010)
Person('bob', 10)

Однако, вызывая его из экземпляра из Person не будет:

>>> Person("bob", 2010).fromBirthYear("bob again", 10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: fromBirthYear() takes 2 positional arguments but 3 were given

Это связано с тем, как тип function реализует протокол дескриптора: доступ через экземпляр вызывает его метод __get__ (который возвращает объект method, который «предварительно передает» экземпляр базовой функции), тогда как доступ через класс возвращает саму функцию.


Чтобы сделать вещи более согласованными, вы можете определить fromBirthYear как метод stati c, который всегда дает доступ к базовой функции вне зависимости от того, осуществляется ли доступ из класса или из экземпляра:

from datetime import date
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @staticmethod
    def fromBirthYear(name, year):
        return Person(name, date.today().year - year)

    def __repr__(self):
        return f"Person('{self.name}', {self.age})"


>>> Person.fromBirthYear("bob", 2010)
Person('bob', 10)
>>> Person.fromBirthYear("bob", 2010).fromBirthYear("bob again", 2015)
Person('bob again', 5)

Наконец, ведет себя метод класса что-то вроде метода stati c, согласованного в получаемых аргументах независимо от того, вызывается ли он из класса или из экземпляра класса. Но, как и метод экземпляра, он получает один неявный аргумент: сам класс, а не экземпляр класса. Преимущество здесь в том, что экземпляр, возвращаемый методом класса, может быть определен во время времени выполнения . Допустим, у вас есть подкласс Person

from datetime import date
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @classmethod
    def fromBirthYear(cls, name, year):
        return cls(name, date.today().year - year)

    def __repr__(self):
        return f"Person('{self.name}', {self.age})"

class DifferentPerson(Person):
    pass

Оба класса можно использовать для вызова fromBirthYear, но возвращаемое значение теперь зависит от класса, который его вызывает.

>>> type(Person.fromBirthYear("bob", 2010))
<class '__main__.Person'>
>>> type(DifferentPerson.fromBirthYear("other bog", 2010))
<class '__main__.DifferentPerson'>
0 голосов
/ 28 мая 2020

Потому что, если вы унаследованы от Person, fromBirthYear всегда будет возвращать объект Person, а не производный класс.

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def fromBirthYear(name, year):
        return Person(name, year)


class Fred(Person):
    pass
print(Fred.fromBirthYear('bob', 2019))

Вывод:

<__main__.Person object at 0x6ffffcd7c88>

Вы бы хотели, чтобы Fred.fromBirthYear был вернуть объект Fred.

В конце концов, язык позволит вам делать много вещей, которые вам не следует делать.

...