【问题标题】:How to cast object in Python如何在 Python 中转换对象
【发布时间】:2012-02-25 02:32:18
【问题描述】:

我有两个无法修改的类(我们称之为 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 中解决。除非我得到一个可行的解决方案,否则我无法接受答案,所以请编写工作代码以获得接受。

【问题讨论】:

  • "Casting" 在 Python 中不存在。
  • 另外,你不应该做 "ReturnStatement().act()" - 如果你想让其他类的 act 方法在当前对象上工作,做 returnStatement.act(self) - 或者只是将它标记为一个类方法或staticmethod - 如果它不需要当前对象的实例。
  • Working.do() 返回一个 ReturnStatement。我希望 MutantWorking.do() 返回一个 MutantReturnStatement。我知道 Python 中不存在强制转换,但问题确实存在。有解决办法吗?
  • 我是否覆盖 MutantReturnStatement 的 __init__() 方法来完成额外的工作并使用 ReturnStatement 返回或覆盖 MutantReturnStatement 的 act() 方法来完成 ReturnStatement 的 act() 方法的工作都没有关系.我无法到达 MutantReturnStatement,因为 Working 返回的是 ReturnStatement 而不是 MutantReturnStatement。

标签: python inheritance casting


【解决方案1】:

正如其他答案已经解释的那样,没有演员表。您可以使用装饰器创建子类或使用额外功能修改新类型。

这是一个完整的示例(感谢 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)

【讨论】:

  • 我想你理解我的问题,到目前为止这似乎是答案。谢谢!
  • 在我最初的热情之后,我很遗憾地意识到这不是解决我问题的方法。我无法使用装饰器修改 Working 类以返回 MutantReturnStatement 而不是 ReturnStatement,因为那是初始条件(无法修改工作)。
  • @Visko,我想你当时误读了我的回答。我的“loghello”正是您要求的“MutantWorking”。您不必修改“工作”,就像我没有修改“你好”一样。如果您认为这仍然不清楚或者您不同意,我可以尝试更新我的答案。
  • @Visko,我重命名了我的方法和类,使其更像您的示例。
  • 也许我误解了一些东西,但它是否返回了一个 MutantReturnState 对象?如果不是,这不是一个可接受的解决方案。
【解决方案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 个 :-))属性要复制,则有 Pythonic 方法可以在 __init__method 上没有很多 self.attr = other.attr 行 - 其中最懒惰的就是简单地复制另一个实例的__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 语言不太熟悉 - 但是任何语言都允许强制转换为子类吗?是否有意义?我认为只允许将 s 转换为父类。

更新:当以 PEP 484 中描述的方式使用类型提示和静态分析时,有时静态分析工具无法弄清楚发生了什么。所以,有typing.cast 调用:它在运行时什么也不做,只是返回传递给它的同一个对象,但是工具然后“学习”返回的对象是传递的类型,并且不会抱怨关于它。它将消除辅助工具中的输入错误,但我不能强调它在运行时没有任何影响:

In [18]: from typing import cast                                                                                                   

In [19]: cast(int, 3.4)                                                                                                            
Out[19]: 3.4

【讨论】:

  • 能否请您在代码中发布答案?否则我认为你没有回答我的问题。
  • @jsbueno,是的,您可以在例如 C++ 中定义从任何对象到任何对象的强制转换。
  • 我不一定知道非突变类的属性。假设它们是导入到 Python 的 C++ 库。
【解决方案3】:

没有直接的方法。

你可以像这样定义 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()

【讨论】:

  • 我是python新手,代码可能有一些错误,但说明思路
  • 我喜欢你的回答!如果没有人告诉另一种(更好的)技术,我会接受这个:)。
  • +1。 @Visko,如果你喜欢的话,+1。 (你可以做不止一次。)此外,使用装饰器你可以做到这一点,而不需要为每个方法制作一个这样的代码块。
  • 这个解决方案仍然不完美。我错过了提到我需要 MutantWorking 必须返回一个 MutantReturnStatement,它的工作原理几乎类似于 ReturnStatement,只是它的 act() 方法被重载。在这个解决方案中,我可以访问 returnStatement 作为一个属性,它改变了我使用 act() 的方式(rs.returnStatement.act() 而不是 rs.act() )。
【解决方案4】:

你不需要在这里投射。你只需要

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

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

【讨论】:

  • 这不是一个好的解决方案,因为我必须返回相同的状态对象(属性未包含在我的示例中以保持简单性)。这会返回一个新实例,这不是我想要的。
【解决方案5】:

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

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() 成员函数。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-12-18
    • 1970-01-01
    • 2022-09-24
    • 2016-05-18
    • 2023-03-27
    • 2021-08-09
    • 2019-11-01
    • 1970-01-01
    相关资源
    最近更新 更多