python class indexing wrapper magic-methods

python - Asignar(en lugar de definir) un método mágico__getitem__ rompe la indexación



class indexing (2)

Deben definirse métodos especiales (esencialmente cualquier cosa con dos guiones bajos en cada extremo) en la clase. El procedimiento de búsqueda interna para métodos especiales omite por completo el dict de instancia. Entre otras cosas, esto es así si lo haces

class Foo(object): def __repr__(self): return ''Foo()''

__repr__ método __repr__ que definió solo se usa para instancias de Foo , y no para repr(Foo) .

Tengo una clase de contenedor similar a este ejemplo (muy simplificado):

class wrap(object): def __init__(self): self._data = range(10) def __getitem__(self, key): return self._data.__getitem__(key)

Puedo usarlo así:

w = wrap() print w[2] # yields "2"

Pensé que podía optimizar y deshacerme de una llamada de función cambiando a esto:

class wrap(object): def __init__(self): self._data = range(10) self.__getitem__ = self._data.__getitem__

Sin embargo, recibo un

TypeError: el objeto ''wrap'' no admite indexación

para la línea print w[2] con la última versión.

La llamada directa al método, es decir, print w.__getitem__(2) , funciona en ambos casos ...

¿Por qué la versión de asignación no permite la indexación?


En realidad, puede resolver esto creando una nueva clase para cada tipo. Si desea que esto funcione de manera transparente, __new__ es el lugar para ello.

import weakref class BigWrap(object): def __new__(cls, wrapped): wrapped_type = type(wrapped) print(''Wrapping %s (%s)'' % (wrapped, wrapped_type)) # creates a new class, aka a new type wrapper_class = type( # new_class = type(class name, base classes, class dict) ''%s_%s_%d'' % (cls.__name__, wrapped_type.__name__, id(wrapped)), # dynamic class name ( cls, # inherit from wrap to have all new methods wrapped_type, # inherit from wrap_type to have all its old methods ), { ''__getitem__'': wrapped.__getitem__, # overwrite __getitem__ based on wrapped *instance* ''__new__'': wrapped_type.__new__, # need to use wrapped_type.__new__ as cls.__new__ is this function }) cls._wrappers[wrapped_type] = wrapper_class # store wrapper for repeated use return cls._wrappers[wrapped_type](wrapped) # self is already an instance of wrap_<type(wrapped)> def __init__(self, wrapped): self.__wrapped__ = wrapped

"Solución" inicial:

import weakref class wrap(object): _wrappers = weakref.WeakValueDictionary() # cache wrapper classes so we don''t recreate them def __new__(cls, wrapped): wrapped_type = type(wrapped) print(''Wrapping %s (%s)'' % (wrapped, wrapped_type)) try: return object.__new__(cls._wrappers[wrapped_type]) # need to use object.__new__ as cls.__new__ is this function except KeyError: print(''Creating Wrapper %s (%s)'' % (wrapped, wrapped_type)) # creates a new class, aka a new type wrapper_class = type( # class name, base classes, class dict ''%s_%s'' % (cls.__name__, wrapped_type.__name__), # dynamic class name (cls,), # inherit from wrap to have all its method {''__getitem__'': wrapped_type.__getitem__}) # overwrite __getitem__ based on wrapped class cls._wrappers[wrapped_type] = wrapper_class # store wrapper for repeated use return cls._wrappers[wrapped_type](wrapped) # self is already an instance of wrap_<type(wrapped)> def __init__(self, wrapped): self._data = wrapped

¡Pero ten cuidado! Esto hará lo que quiera: use la clase envuelta '' __getitem__ . Sin embargo, esto no siempre tiene sentido. Por ejemplo, la list.__getitem__ está realmente integrada en el CAPI de CPython y no es aplicable a otros tipos.

foo = wrap([1,2,3]) print(type(foo)) # __main__.wrap_list foo[2] --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-31-82791be7104b> in <module>() ----> 1 foo[2] TypeError: descriptor ''__getitem__'' for ''list'' objects doesn''t apply to ''wrap_list'' object