如何在Python中转换对象

时间:2012-02-02 12:26:51

标签: python inheritance casting

我有两个类(我们称之为Working和ReturnStatement),我无法修改它,但我想用日志记录扩展它们。诀窍是Working的方法返回一个ReturnStatement对象,因此新的MutantWorking对象也返回ReturnStatement,除非我可以将它强制转换为MutantReturnStatement。用代码说:

# 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

预期结果:
我正在包装工作。
我在工作。
-
我正在包裹ReturnStatement 我是一个ReturnStatement。

有可能解决问题吗?如果问题也可以在PHP中解决,我也很好奇。除非我得到一个有效的解决方案,否则我无法接受答案,所以请编写工作代码以便接受。

5 个答案:

答案 0 :(得分:7)

由于其他答案已经解释,因此没有演员表。您可以使用装饰器来创建子类或使用额外功能修改新类型。

这是一个完整的例子(归功于How to make a chain of function decorators?)。您无需修改​​原始类。在我的示例中,原始类称为Working。

# 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()

这将打印

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

另外,我想了解为什么你不能修改一个类。你试过了吗?因为,作为创建子类的替代,如果您感觉动态, 几乎总能修改旧类:

Working.Do=logging(Working.Do)
ReturnStatement.Act=logging(ReturnStatement.Act)

更新:将记录应用于类的所有方法

正如你现在特别要求的那样。您可以遍历所有成员并将记录应用于所有成员。但是您需要为要修改的成员类型定义规则。以下示例排除了名称中包含__的任何方法。

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

有了这一切,你需要做的就是创建一个类的新记录版本:

@loggify
class loggedWorker(Working): pass

或者修改现有的类:

loggify(Working)

答案 1 :(得分:2)

Python中没有“强制转换”。 类的任何子类都被视为其父类的实例。通过适当调用超类方法和覆盖类属性,可以实现所需的行为。

你可以对你的例子做些什么,就是必须有一个接收超类并复制其相关属性的子类初始化器 - 所以,你的MutantReturnstatement可以写成:

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类更改为:

class MutantWorking(Working):
    def do(self):
        print "I am wrapping Working."
        return MutantReturnStatement(Working().do())

如果有多个(例如,超过3 :-))属性要复制,那么在self.attr = other.attr方法中没有很多__init__行的Pythonic方法 - 其中最简单的只是复制另一个实例的__dict__属性。

或者,如果你知道自己在做什么,你也可以简单地将目标对象的__class__属性更改为所需的类 - 但这可能会产生误导并带你到微妙的错误(子类的__init__方法不会被调用,不适用于非python定义的类,以及其他可能的问题),我不推荐这种方法 - 这不是“铸造”,它使用内省来强制对象更改,仅用于保持答案完整:

class MutantWorking(Working):
    def do(self):
        print "I am wrapping Working."
        result = Working.do(self)
        result.__class__ = MutantReturnStatement
        return result

再次 - 这应该有效,但不要这样做 - 使用前一种方法。

顺便说一句,我对其他允许转换的OO语言没有太多经验 - 但是即使在任何语言中都允许转换为子类?是否有意义?我认为转换只允许父类。

答案 2 :(得分:1)

没有直接的方式。

您可以像这样定义MutantReturnStatement的 init

def __init__(self, retStatement):
    self.retStatement = retStatement

然后像这样使用它:

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())

你应该摆脱在你的包装器中继承ReturnStatement,就像这样

class MutantReturnStatement(object):
    def act(self):
        print "I'm wrapping ReturnStatement."
        return self.retStatement.act()

答案 3 :(得分:0)

你不需要在这里施法。你只需要

class MutantWorking(Working):
def do(self):
    print "I am wrapping Working."
    Working().do()
    return MutantReturnStatement()

这显然会给出正确的回报和所需的打印输出。

答案 4 :(得分:0)

您所做的不是强制转换,而是类型转换。不过,您可以编写类似

的内容
def cast_to(mytype: Type[any], obj: any):
    if isinstance(obj, mytype):
        return obj
    else:
        return mytype(obj)

class MutantReturnStatement(ReturnStatement):
    def __init__(self, *args, **kwargs):
        if isinstance(args[0], Working):
            pass  
            # your custom logic here 
            # for the type conversion.

用法:

cast_to(MutantReturnStatement, Working()).act()
# or simply
MutantReturnStatement(Working()).act()

(请注意,在您的示例MutantReturnStatement中,没有.do()成员函数。)