关于继承:如何在Python中转换对象

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个),那么在__init__方法上有很多没有self.attr = other.attr行的pythonic方法。-其中最懒惰的就是复制另一个实例的__dict__属性。

或者,如果您知道自己在做什么,您也可以简单地将目标对象的__class__属性更改为所需的类,但这可能会误导您,并导致细微的错误(子类的__init__方法不会被调用,也不会在非python定义的类上工作,以及其他可能的问题),i不要重新推荐这种方法-这不是"强制转换",而是使用内省来残酷地改变对象,并且只包括保持答案完整:

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

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