2015-04-24 3 views
2

Чисто любопытство вопрос:Переопределение списка встроенных команд динамически в области видимости класса

class Li(list): pass 
m, n= Li([1]), Li([2]) 
def r(*args, **kwargs): raise Exception('hop') 
setattr(m, '__iadd__', r) 
m += n 
print m # [1, 2] 
setattr(Li, '__iadd__', r) 
m += n 

Выход:

[1, 2] 
Traceback (most recent call last): 
    File "C:\...\test_override.py", line 8, in <module> 
    m+=n 
    File "C:\...\test_override.py", line 3, in r 
    def r(*args, **kwargs): raise Exception('hop') 
Exception: hop 

Если я использую setattr(m, 'append', r) тогда m.append(2) подведет. Итак, __iadd__ вызывает объект класса?

Также можно использовать settattr из класса scope? Наивная попытка, как в:

def r(*args, **kwargs): raise Exception('hop') 
class Li(list): 
    for s in {'__iadd__','append'}: setattr(Li, s, r) 

терпит неудачу с NameError: имя «Ли» не определен

+0

Не могли бы вы разъяснить, что вы на самом деле спрашиваете? Если вы хотите исправлять встроенные модули, вам понадобится что-то вроде https://github.com/clarete/forbiddenfruit – jonrsharpe

+0

. Имена специальных методов [искали] (https://docs.python.org/2/reference/ datamodel.html # special-method-lookup-for-new-style-classes) в типе, а не в словаре экземпляров. Вот почему это связано с 'append', но не с' __iadd__'. – mata

+0

@mata: aha - не могли бы вы объяснить ответ (с таким количеством ссылок, сколько хотите) - также (и это сложная часть), как я могу это сделать из класса? Это динамически вызывает 'setattr' раз и навсегда при инициализации класса? Забудьте о прецеденте - я спрашиваю, возможно ли это –

ответ

2

В новых классах стиль, метод экземпляра __getattr__ дольше не перехватывает вызовы, сделанные встроенных операций. Когда используется встроенная операция, поиск начинается с уровня класса, а не на уровне экземпляра. Тем не менее, явный вызов имя метода будет работать:

>>> class Li(list): pass 
>>> m, n= Li([1]), Li([2]) 
>>> def r(*args, **kwargs): 
     raise Exception('hop') 

>>> setattr(m, '__iadd__', r) 
>>> m += n   # no exception raised 

>>> m.__iadd__(n) # explicitly calling the method searches the instance first 
Traceback (most recent call last): 
    File "<pyshell#76>", line 1, in <module> 
    m.__iadd__(n) 
    File "<pyshell#73>", line 1, in r 
    def r(*args, **kwargs): raise Exception('hop') 
Exception: hop 

Один из способов для достижения вашей второй целью является использование a metaclass, особенно если вы хотите создать атрибуты один раз (в соответствии с вашими комментариями):

>>> class Meta(type): 
    def __new__(meta, name, bases, attrdict): 
     print 'adding class attributes!' 
     for s in {'__iadd__','append'}: 
      attrdict[s] = r 
     return super(Meta, meta).__new__(meta, name, bases, attrdict) 
    def __init__(cls, name, bases, attrdict): 
     super(Meta, cls).__init__(name, bases, attrdict) 


>>> class Li(list): 
    __metaclass__ = Meta 


adding class attributes!   # attributes added here 
>>> t = Li()      # but not on instance creation 
>>> Li.__dict__ 
dict_proxy({'__module__': '__main__', '__metaclass__': <class '__main__.Meta'>, '__iadd__': <function r at 0x02AAA270>, '__dict__': <attribute '__dict__' of 'Li' objects>, '__weakref__': <attribute '__weakref__' of 'Li' objects>, '__doc__': None, 'append': <function r at 0x02AAA270>}) 
+0

Спасибо за: вторая часть - я хочу сделать это _once_, статически - я уже делаю это для каждого экземпляра в '__init__', но он вонючий (на _every_ instance создание). Отказ от ответственности: любопытство :) –

+0

Не будет 'for s in {'__iadd __', 'append'}: setattr (cls, s, r)' запускается при создании экземпляра evey ('Li()')? –

+0

Ах. Подождите. Я вижу, да. Один момент. –

Смежные вопросы