Могу ли я передать имя атрибута при его инициализации? - PullRequest
3 голосов
/ 15 апреля 2020

У меня есть класс, в котором я определяю несколько атрибутов класса как объекты. Имя самого атрибута является одним из аргументов, которые необходимо передать объекту. В настоящее время они пишутся вручную, как в приведенном ниже примере:

class Beatles:
    john = Guitar(name='john')
    paul = Bass(name='paul')
    george = Guitar(name='george')
    ringo = Drums(name='ringo')

class Musician:
    def __init__(self)

class Guitar(Musician):
    def __init__(self, name)

class Bass(Musician):
    def __init__(self, name)

class Drums(Musician):
    def __init__(self, name) 

Я нашел один обходной путь, который использует метакласс для построения объектов, используя словарь пространства имен объекта Foo:

class Meta(type):
    def __new__(cls, name, bases, dict_):
        for k, v in dict_.items():
            if isinstance(v, Musician):
                dict_[k] = type(v)(k)
        return super().__new__(cls, name, bases, dict_)

Это работает, только если аргументы 'name' являются единственными аргументами, которые не будут иметь место. Есть ли лучший способ?

Ответы [ 2 ]

1 голос
/ 15 апреля 2020

В Python 3.6 появилась новая функция для решения этой модели, и dimini sh требует метаклассов, поскольку они почти всегда приводят к путанице.

Все, что нужно, - это чтобы классы объектов, которые должны быть атрибутами, имели метод __set_name__ (в вашем случае это все подклассы Musician, поэтому все, что вам нужно сделать, это чтобы добавить def __set_name__(self, owner, name): ... метод к нему).

Итак, все, что вам нужно в вашем случае:


class Musician:
    def __init__(self, ...):
        # no need to get a 'name' parameter here
        ...
    # This is called automatically by Python:
    def __set_name__(self, owner, name):
        # 'owner' is the class object where the attributes are defined in. 
        # in this case, "Beatles". It is usually not needed, but available.

        self.name = name

class Guitar(Musician):
    pass

class Bass(Musician):
    pass

class Drums(Musician):
    pass

class Beatles(metaclass=Meta):
    john = Guitar()
    paul = Bass()
    george = Guitar()
    ringo = Drums()

Теперь, если по какой-то причине вы хотите реализовать это с помощью метаклассы (допустим, вы должны работать с Python 3.5 или не можете изменить код для классов Musician) - вы можете использовать functools.partial для хранения других атрибутов и просто передать отсутствующий атрибут name в тот же код метакласса, который у вас есть выше:

from functools import partial 

class Beatles:
    john = partial(Guitar, other_attribute='')
    paul = partial(Bass, wearing_shoes=False)
    george = partial(Guitar)
    ringo = partial()

(И имейте в виду, что при желании вы можете сократить partial для удобства чтения, с такими простыми вещами, как from functools import partial as P)

0 голосов
/ 15 апреля 2020

Сам объект еще не существует, но конструктор, в конечном счете, является функцией, поэтому вы передаете то, что вам нужно, в качестве аргумента функции.

В этом случае пример может не передавать вопрос Вы хотите спросить. У вас нет имени, потому что вы барабанщик, у вас есть имя, потому что вы человек. В качестве верхнего уровня иерархии у Musician будет это свойство, а не Drummer, и это должен быть параметр в конструкторе Musician:

class Musician:
    def __init__(self, name):
        self.name = name

class Guitar(Musician):
    def __init__(self, name):
        super().__init__(name)

class Bass(Musician):
    def __init__(self, name):
        super().__init__(name)

class Drums(Musician):
    def __init__(self, name):
        super().__init__(name)

Beatles = {
    Guitar(name='john'),
    Bass(name='paul'),
    Guitar(name='george'),
    Drums(name='ringo'),
}        

for m in Beatles:
    print (m.name)
...