|
1
1
要做到这一点,你必须重新定义
相同的基本转换包装器可以用于所有属性和方法返回值。它只需要在调用
下面显示的解决方案正是这样做的。它显式地包装所有未列为例外的dunder。所有其他属性要么立即强制转换,要么包装(如果它们是函数)。它允许通过检查
import functools
def isdunder(x):
return isinstance(x, str) and x.startswith('__') and x.endswith('__')
class DunderSet:
def __contains__(self, x):
return isdunder(x)
def wrap_method(method, xtype, cast):
@functools.wraps(method)
def retval(*args, **kwargs):
result = method(*args, **kwargs)
return cast(result) if type(result) == xtype else result
return retval
def wrap_getter(method, xtype, cast, exceptions):
@functools.wraps(method)
def retval(self, name, *args, **kwargs):
result = method(self, name, *args, **kwargs)
return result if name in exceptions else check_type(result, xtype, cast)
return retval
def check_type(value, xtype, cast):
if type(value) == xtype:
return cast(value)
if callable(value):
return wrap_method(value, xtype, cast)
return value
class ClosedMeta(type):
def __new__(meta, name, bases, dct, **kwargs):
if 'exceptions' in kwargs:
exceptions = set([
'__new__', '__init__', '__del__',
'__init_subclass__', '__instancecheck__', '__subclasscheck__',
*map(str, kwargs.pop('exceptions'))
])
else:
exceptions = DunderSet()
target = kwargs.pop('target', bases[0] if bases else object)
cls = super().__new__(meta, name, bases, dct, **kwargs)
for base in cls.__mro__:
for name, item in base.__dict__.items():
if isdunder(name) and (base is cls or name not in dct) and callable(item):
if name in ('__getattribute__', '__getattr__'):
setattr(cls, name, wrap_getter(item, target, cls, exceptions))
elif name not in exceptions:
setattr(cls, name, wrap_method(item, target, cls))
return cls
def __init__(cls, *args, **kwargs):
return super().__init__(*args)
class MyInt(int):
def __contains__(self, x):
return x == self
def my_op(self, other):
return int(self * self // other)
class ClosedInt(MyInt, metaclass=ClosedMeta, target=int,
exceptions=['__index__', '__int__', '__trunc__', '__hash__']):
pass
class MyClass(ClosedInt, metaclass=type):
def __add__(self, other):
return 1
print(type(MyInt(1) + MyInt(2)))
print(0 in MyInt(0), 1 in MyInt(0))
print(type(MyInt(4).my_op(16)))
print(type(ClosedInt(1) + ClosedInt(2)))
print(0 in ClosedInt(0), 1 in ClosedInt(0))
print(type(ClosedInt(4).my_op(16)))
print(type(MyClass(1) + ClosedInt(2)))
最后一个例子是对 @wim's answer . 这说明你必须 为了让它工作。 IDEOne链接,因为我现在无法访问计算机: https://ideone.com/iTBFW3 附录1:改进的违约例外 special method names 文件的一节。方法可以分为两大类:一类是具有使python机器工作的非常特定的返回类型的方法,另一类是在返回您感兴趣的类型的实例时应检查并包装其输出的方法。还有第三类,就是应该始终排除的方法,即使您忘记显式地提到它们。
以下是默认情况下应排除的所有内容的列表:
附录2:改进的目标
应该可以很容易地针对多种类型。这在扩展的其他实例时特别有用
做
现在替换所有出现的
|
|
2
2
我认为使用一个类修饰符和一个不应该返回相同类型对象的方法黑名单会更像python:
以便:
将输出:
作为奖励,装饰师也可以有选择地用在各种方法上:
|
|
3
1
这是无法做到的,数据模型禁止这样做。我可以证明给你看:
加法首先由left hand对象处理,如果left类型处理它(即不返回
|
|
4
1
每个人都在写简短的代码和元类,而我几乎没有写装饰。(该死的,哈哈)但我还是要和你分享。
现在,在完成了这么长的设置之后,您只需像平常一样装饰类;我太困了,无法让它与继承的类一起工作,因此现在您必须装饰从封闭类继承的类。
请随便投反对票,因为我还不确定我是否正确理解了这个问题。 |
|
|
5
1
我还是觉得可能还有更多 这是一个很好的方法,但我能够修复问题中提供的尝试。
代码
输出
这仍然有一些问题。举例来说,我们仍然有一项繁琐的任务,即检查所有的dunder方法以及实现规则的标记例外,但是除非在某个地方有这些方法的列表,否则这似乎是不可避免的。 |