我有一个类似于这个(强烈简化的)示例的包装类:
class wrap(object):
def __init__(self):
self._data = range(10)
def __getitem__(self, key):
return self._data.__getitem__(key)
我可以这样使用它:
w = wrap()
print w[2] # yields "2"
我认为我可以通过更改为:
来优化和删除一个函数调用class wrap(object):
def __init__(self):
self._data = range(10)
self.__getitem__ = self._data.__getitem__
但是,我收到了
TypeError:'wrap'对象不支持索引
表示后一版本的print w[2]
行。
直接调用该方法,即print w.__getitem__(2)
,在两种情况下都有效......
为什么分配版本不允许编制索引?
答案 0 :(得分:5)
必须在类上定义特殊方法(基本上每端都有两个下划线)。特殊方法的internal lookup procedure完全跳过实例dict。除此之外,如果你这样做是这样的
class Foo(object):
def __repr__(self):
return 'Foo()'
您定义的__repr__
方法仅用于Foo
的实例,而不用于repr(Foo)
。
答案 1 :(得分:1)
您实际上可以通过为每种类型创建一个新类来解决这个问题。如果您希望透明地工作,__new__
就是它的地方。
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
初始“解决方案”:
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
但要小心!这将做你想要的 - 使用包装的类'__getitem__
。但是,这并不总是有意义的!例如,list.__getitem__
实际上内置于CPython的CAPI中,不适用于其他类型。
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