How to cast object in Python
我有两个类(我们称之为Working和ReturnStatement),我不能修改它们,但是我想用日志扩展这两个类。诀窍是,Working的方法返回一个ReturnStatement对象,所以新的mutantWorking对象也返回ReturnStatement,除非我可以将其强制转换为mutantreturnStatement。用代码说:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # these classes can't be changed class ReturnStatement(object): def act(self): print"I'm a ReturnStatement." class Working(object): def do(self): print"I am Working." return ReturnStatement() # these classes should wrap the original ones class MutantReturnStatement(ReturnStatement): def act(self): print"I'm wrapping ReturnStatement." return ReturnStatement().act() class MutantWorking(Working): def do(self): print"I am wrapping Working." # !!! this is not working, I'd need that casting working !!! return (MutantReturnStatement) Working().do() rs = MutantWorking().do() #I can use MutantWorking just like Working print"--" # just to separate output rs.act() #this must be MutantReturnState.act(), I need the overloaded method |
预期结果:
我正在包装工作。
我在工作。
--
我正在包装退货声明。
我是一个返回声明。
有可能解决这个问题吗?我也很好奇这个问题是否也可以用PHP来解决。除非我得到一个有效的解决方案,否则我不能接受这个答案,所以请写下有效的代码来被接受。
其他答案已经解释过了,没有演员阵容。您可以使用decorator创建子类或使用额外的功能创建修改后的新类型。
这是一个完整的例子(归功于如何建立一个功能链装饰师?)。您不需要修改原始类。在我的示例中,原始类称为Working。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # decorator for logging def logging(func): def wrapper(*args, **kwargs): print func.__name__, args, kwargs res = func(*args, **kwargs) return res return wrapper # this is some example class you do not want to/can not modify class Working: def Do(c): print("I am working") def pr(c,printit): # other example method print(printit) def bla(c): # other example method c.pr("saybla") # this is how to make a new class with some methods logged: class MutantWorking(Working): pr=logging(Working.pr) bla=logging(Working.bla) Do=logging(Working.Do) h=MutantWorking() h.bla() h.pr("Working") h.Do() |
。
这将打印
1 2 3 4 5 6 7 8 9 10 | h.bla() bla (<__main__.MutantWorking instance at 0xb776b78c>,) {} pr (<__main__.MutantWorking instance at 0xb776b78c>, 'saybla') {} saybla pr (<__main__.MutantWorking instance at 0xb776b78c>, 'Working') {} Working Do (<__main__.MutantWorking instance at 0xb776b78c>,) {} I am working |
另外,我想知道为什么你不能修改一个类。你试过了吗?因为,作为创建子类的替代方法,如果您感到有活力,几乎可以在适当的位置修改一个旧类:
1 2 | Working.Do=logging(Working.Do) ReturnStatement.Act=logging(ReturnStatement.Act) |
。
更新:将日志记录应用于类的所有方法
正如你现在特别要求的。您可以循环所有成员,并将日志记录应用于所有成员。但是您需要为要修改的成员类型定义一个规则。下面的示例排除了名称中有_uuu的任何方法。
1 2 3 4 5 6 7 8 9 10 | import types def hasmethod(obj, name): return hasattr(obj, name) and type(getattr(obj, name)) == types.MethodType def loggify(theclass): for x in filter(lambda x:"__" not in x, dir(theclass)): if hasmethod(theclass,x): print(x) setattr(theclass,x,logging(getattr(theclass,x))) return theclass |
有了这些,您需要做的就是创建一个类的新的日志版本:
1 2 | @loggify class loggedWorker(Working): pass |
。
或就地修改现有类:
1 | loggify(Working) |
号
在Python中没有"强制转换"。类的任何子类都被视为其父类的实例。通过正确调用超类方法和重写类属性,可以实现所需的行为。
您在示例中可以做的是,必须有一个子类初始值设定项,它接收超类并复制其相关属性-因此,可以这样编写mutantereturn语句:
1 2 3 4 5 6 7 8 | class MutantReturnStatement(ReturnStatement): def __init__(self, previous_object=None): if previous_object: self.attribute = previous_object.attribute # repeat for relevant attributes def act(self): print"I'm wrapping ReturnStatement." return ReturnStatement().act() |
然后将您的mutantworking类更改为:
1 2 3 4 | class MutantWorking(Working): def do(self): print"I am wrapping Working." return MutantReturnStatement(Working().do()) |
号
如果要复制的属性很多(比如,超过3个),那么在
或者,如果您知道自己在做什么,您也可以简单地将目标对象的
1 2 3 4 5 6 | class MutantWorking(Working): def do(self): print"I am wrapping Working." result = Working.do(self) result.__class__ = MutantReturnStatement return result |
同样-这应该有效,但不要这样做-使用以前的方法。
顺便说一下,我对其他允许强制转换的OO语言没有太多经验,但是强制转换到子类在任何语言中都是允许的吗?这有道理吗?我认为施法只允许给家长上课。
没有直接的方法。
您可以这样定义mutatreturnstatement的init:
1 2 | def __init__(self, retStatement): self.retStatement = retStatement |
。
然后像这样使用:
1 2 3 4 5 | class MutantWorking(Working): def do(self): print"I am wrapping Working." # !!! this is not working, I'd need that casting working !!! return MutantReturnStatement(Working().do()) |
您应该避免继承包装器中的返回语句,如下所示
1 2 3 4 | class MutantReturnStatement(object): def act(self): print"I'm wrapping ReturnStatement." return self.retStatement.act() |
。
你不需要在这里铸造。你只是需要
1 2 3 4 5 | class MutantWorking(Working): def do(self): print"I am wrapping Working." Working().do() return MutantReturnStatement() |
号
这显然会给出正确的返回和所需的打印输出。