Являются ли бесклассовые методы в Python полезными для чего-либо? - PullRequest
9 голосов
/ 30 ноября 2011

Я только что заметил, что вы можете сделать это в Python:

def f(self):
    print self.name

class A:
    z=f
    name="A"

class B:
    z=f
    name = "B"

...

print a.z()

>>> A

Другими словами, f() ведет себя как метод, который не определен ни в одном классе, но может быть присоединен к одному. И, конечно, он выдаст ошибку времени выполнения, если он ожидает методы или поля объекта, к которому он прикреплен и которого не существует.

Мой вопрос: это полезно? Это служит цели? Есть ситуации, когда это решает проблему? Может быть, это способ определения интерфейсов?

Ответы [ 3 ]

6 голосов
/ 30 ноября 2011

Да, это полезно и служит определенной цели, но это также довольно редко приходится делать.Если вы считаете, что вам нужно исправлять классы после того, как они были определены, вы всегда должны остановиться и подумать, действительно ли это лучший способ.

Одной из ситуаций является исправление обезьян.Я сделал это в большой системе Plone, где некоторым методам требовались незначительные изменения, но просто не было простого способа переопределить поведение в обычном режиме.В той ситуации, когда у вас есть сложная библиотека, она предоставляет простой способ внедрить новое или измененное поведение без необходимости изменять исходную библиотеку.

Другая ситуация, которая приходит на ум, - это когда вам нужно много методов, которыеможет быть создан автоматически.например, тесты, управляемые данными.

def createTest(testcase, somedata, index):
    def test(self):
         "Do something with somedata and assert a result"
    test_name = "test_%d" % index
    setattr(testcase, test_name, test)

for index, somedata in enumerate(somebigtable):
    createTest(MyTestCase, somedata, index)

, когда MyTestCase является unittest.TestCase, у вас может быть один тест, который проходит через все данные, но останавливается при первом сбое, и вам нужно попытаться выяснить, какойстрока данных не удалась.При динамическом создании методов все тесты выполняются отдельно, а имя теста говорит вам, какой из них не выполнен (оригинал кода выше фактически создал более значимое имя, включающее некоторые данные, а также индекс).

Вы не можете сделать это внутри тела класса, потому что нет никакого способа ни ссылаться на сам класс, ни на его словарь до завершения определения.Однако вы можете сделать что-то похожее с метаклассом, так как это позволит вам изменить dict класса перед созданием самого класса, а иногда это более чистый способ сделать то же самое.

Еще одна вещь, на которую стоит обратить внимание:Есть ситуации, когда это не сработает.Некоторые __xxx__ специальные методы не могут быть переопределены после создания класса: исходное определение сохраняется внутри где-то, кроме __dict__ класса, поэтому любые изменения, которые вы сделаете позже, могут быть проигнорированы.Также при работе с метаклассами иногда дополнительные функции не получают никакой обработки, которую метакласс дает атрибутам как часть определения класса.

3 голосов
/ 30 ноября 2011

Поскольку у объектов функций есть метод __get __ (...), это дескриптор без данных.

Когда вы определяете метод в классе A, это просто функциональный объект в A .__ dict __:

In [78]: class A(object):
   ....:     def f1(self):
   ....:         pass
   ....: 

In [79]: A.__dict__["f1"]
Out[79]: <function f1 at 0x0D39A070>

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

In [80]: A.f1
Out[80]: <unbound method A.f1>

In [81]: A().f1
Out[81]: <bound method A.f1 of <__main__.A object at 0x0D2F1CD0>>

Так работает метод. Итак, вы можете добавить метод позже:

In [82]: A.f2 = lambda self: id(self)

In [83]: A().f2()
Out[83]: 221189392

Итак, ваш код почти такой же, как:

class A:
    def z(self):
        print self.name
    name="A"

Пожалуйста, прочитайте подробности из http://docs.python.org/howto/descriptor.html

2 голосов
/ 30 ноября 2011

Интересные факты:

  1. Функция в python - это тип:

    type(f)  # prints <type: function>
    
  2. Итак, функция является вызываемым экземпляром, поэтому ее можно подключить куда угодно.

  3. Может использоваться для переноса функций и эмуляции функций plug and play.

  4. Это может быть очень удобно во время разработки - для опробования различных логик (с помощью подключения различных функций)

  5. Это обеспечивает абстракцию над логикой, поскольку мы можем заменить методы класса импортированными функциями.

Хотя я сомневаюсь, что как вы будете использовать его для эмуляции интерфейса!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...