Как сгенерировать статический метод из метакласса? - PullRequest
0 голосов
/ 26 октября 2018

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

class PlayMeta(type):
    def __new__(cls, clsname, bases, dct):
        newclass = super(PlayMeta, cls).__new__(cls, clsname, bases, dct)

        @staticmethod
        def make():
            print('meta make')

        setattr(newclass, make.__name__, make)

        return newclass

class Fritz(PlayMeta):
    pass

Fritz.make()

, но когда я запускаю его, я получаю:

AttributeError: type object 'Fritz' has no attribute 'make'

Как вы это делаете?У меня есть сообщения с большим количеством полей, и я хочу, чтобы это был класс, который будет создавать и генерировать разные из этих сообщений.Этот вопрос о фабрике / создании части.Я использую Python 3.5.Спасибо!

правка: 2-я, рабочая версия:

class PlayMeta(type):
    def __new__(cls, clsname, bases, dct):
        newclass = super(PlayMeta, cls).__new__(cls, clsname, bases, dct)

        def instance_method(self, x, y, z):
            print('instance method', x, y, z)
        setattr(newclass, instance_method.__name__, instance_method)

        def static_make(x, y, z):
            print('static make', x, y, z)
        setattr(newclass, static_make.__name__, static_make)

        return newclass

class Fritz(metaclass=PlayMeta):
    pass

Fritz.static_make('a', [], 3)

f = Fritz()
f.instance_method('a', [], 3)

Успешно печатает:

static make a [] 3
instance method a [] 3

Как Python отличает метод экземпляра от метода класса / статики,когда они определены и прикреплены одинаково?Наличие слова «я»?Потому что это было бы удобно, но немного нелепо.

edit2: Ах, это, вероятно, только в том, как это называется - вызывайте его с экземпляром, и все хорошо с методом, ожидающим экземпляр.Но вы можете вызвать любой метод, с экземпляром или без него, и они будут вести себя правильно или нет, соответственно.

edit3, окончательная форма :):

@staticmethod
def static_make(x, y, z):
    print('static make', x, y, z)
    setattr(newclass, 'static_make', static_make)

Я не знаю, что (в) staticmethod добавляет - версия 2, выше, сработало - но это больше самодокументируетсяпо крайней мере.

1 Ответ

0 голосов
/ 26 октября 2018

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

class Fritz(metaclass=PlayMeta):
    pass

Что касается различия между статическими и экземплярами методов, все обычные функции ведут себя как методы экземпляра и при вызове экземпляра получат его в качестве первого позиционного аргумента (независимо от того, как он назван). Вы можете попробовать в своем 2-м примере позвонить f.static_method или Fritz.instance_method, они ведут себя одинаково. Чтобы узнать больше, взгляните на дескрипторы .

Декоратор staticmethod преобразует обычную функцию в надлежащий статический метод, который будет работать одинаково даже при вызове экземпляра (то есть он не получит экземпляр в качестве первого аргумента). Но обратите внимание, что при применении результирующий объект не имеет атрибута __name__, который вам понадобится, если вы будете генерировать эти методы программно.

Кроме того, аргумент dct для __new__ - это набор имен и значений, которые будут использоваться для установки атрибутов нового класса, так что это может быть хорошим местом для размещения методов, которые вы создаете, в вашем метаклассе. , Это также позволит вам решить, хотите ли вы, чтобы класс Fritz мог переопределять атрибуты из метакласса или наоборот.

Пример (метакласс переопределяет методы, если они также определены в классе):

class PlayMeta(type):
    def __new__(cls, clsname, bases, dct):

        def instance_method(self, x, y, z):
            print('instance method', x, y, z)

        def static_make(x, y, z):
            print('static make', x, y, z)

        return super(PlayMeta, cls).__new__(cls, clsname, bases, {**dct,
            instance_method.__name__: instance_method,
            static_make.__name__: staticmethod(static_make),
        })


class Fritz(metaclass=PlayMeta):
    ...


f = Fritz()

...

In [2]: f.instance_method(1, 2, 3)
instance method 1 2 3

In [3]: f.static_make(1, 2, 3)
static make 1 2 3

In [4]: Fritz.static_make(1, 2, 3)
static make 1 2 3

In [5]: Fritz.instance_method(1, 2, 3, 4)
instance method 2 3 4
...