Довольно просто использовать специальный метод __getattr__ в классах Python для обработки либо отсутствующих свойств, либо функций, но, по-видимому, не оба одновременно.

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

class Props:
    def __getattr__(self, attr):
        return 'some_new_value'

>>> p = Props()
>>> p.prop                          # Property get handled
'some_new_value'

>>> p.func('an_arg', kw='keyword')  # Function call NOT handled
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: 'str' object is not callable

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

class Funcs:
    def __getattr__(self, attr):
        def fn(*args, **kwargs):
            # Do something with the function name and any passed arguments or keywords
            print attr
            print args
            print kwargs
            return 
        return fn

>>> f = Funcs()
>>> f.prop                          # Property get NOT handled
<function fn at 0x10df23b90>

>>> f.func('an_arg', kw='keyword')  # Function call handled
func
('an_arg',)
{'kw': 'keyword'}

Вопрос в том, как обрабатывать оба типа отсутствующих атрибутов в одном и том же __getattr__? Как определить, был ли запрошенный атрибут в нотации свойства или в нотации метода с круглыми скобками и вернуть либо значение, либо функцию соответственно? По сути, я хочу обработать НЕКОТОРЫЕ отсутствующие атрибуты свойств И НЕКОТОРЫЕ отсутствующие атрибуты функции, а затем прибегнуть к поведению по умолчанию для всех остальных случаев.

Совет?

3
sansjoe 30 Янв 2013 в 23:49

2 ответа

Лучший ответ

Как определить, был ли запрошенный атрибут в нотации свойства или в нотации метода с круглыми скобками и вернуть либо значение, либо функцию соответственно?

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

Итак, вам нужен какой-то внеполосный способ узнать, создавать ли функцию или какой-либо другой тип значения. На самом деле это довольно часто - вы можете использовать прокси для какого-то другого объекта, который имеет имеет различие значения / функции (например, ctypes или PyObjC), или у вас может быть соглашение об именовании, и т.п.

Однако вы всегда можете вернуть объект, который можно использовать в любом случае. Например, если ваше «поведение по умолчанию» - возвращать атрибуты, являющиеся целыми числами, или функции, которые возвращают целое число, вы можете вернуть что-то вроде этого:

class Integerizer(object):
    def __init__(self, value):
        self.value = value
    def __int__(self):
        return self.value
    def __call__(self, *args, **kw):
        return self.value
2
abarnert 30 Янв 2013 в 20:05

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

>>> class Foo(object):
...     def bar(self): print 'bar called'
...     spam='eggs'
... 
>>> Foo.bar
<unbound method Foo.bar>
>>> Foo.spam
'eggs'

Python сначала ищет атрибут (bar или spam), и если вы хотели вызвать его (добавили круглые скобки), то Python вызывает вызываемый после поиск атрибута:

>>> foo = Foo()
>>> fbar = foo.bar
>>> fbar()
'bar called'

В приведенном выше коде я отделил поиск bar от вызова bar.

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

__getattr__ вызывается всякий раз, когда происходит сбой нормального доступа к атрибуту; в следующем примере monty определен в классе, поэтому __getattr__ вызывается не ; он вызывается только для bar.eric и bar.john:

>>> class Bar(object):
...     monty = 'python'
...     def __getattr__(self, name):
...         print 'Attribute access for {0}'.format(name)
...         if name == 'eric':
...             return 'idle'
...         raise AttributeError(name)
... 
>>> bar = Bar()
>>> bar.monty
'python'
>>> bar.eric
Attribute access for eric
'idle'
>>> bar.john
Attribute access for john
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in __getattr__
AttributeError: john

Обратите внимание, что функции - не единственные объекты, которые вы можете вызывать (вызывать); любой пользовательский класс, который реализует метод __call__, будет делать:

>>> class Baz(object):
...    def __call__(self, name):
...        print 'Baz sez: "Hello {0}!"'.format(name)
...
>>> baz = Baz()
>>> baz('John Cleese')
Baz sez: "Hello John Cleese!"

Вы можете использовать эти возвращаемые объекты из __getattr__, которые можно вызывать и использовать в качестве значения в разных контекстах.

1
Martijn Pieters 30 Янв 2013 в 20:04