Python 使用元类替换类定义?

Python 使用元类替换类定义?,python,metaclass,monkeypatching,Python,Metaclass,Monkeypatching,Python 3.6 我正在尝试修改第三方库的行为 我不想直接更改源代码 考虑到以下代码: class UselessObject(object): pass class PretendClassDef(object): """ A class to highlight my problem """ def do_something(self): # Allot of code here result = Usel

Python 3.6

我正在尝试修改第三方库的行为

我不想直接更改源代码

考虑到以下代码:

class UselessObject(object):
    pass


class PretendClassDef(object):
    """
    A class to highlight my problem
    """

    def do_something(self):

        # Allot of code here

        result = UselessObject()

        return result
我想用自己的类替换
UselessObject

我想知道在我的模块中使用元类来拦截
UselessObject
的创建是否是一个有效的想法

编辑

Ashwini Chaudhary就同一问题发布的答案可能对其他人有用。以及下面的答案


另外,我还发现“模块”级
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu元类
在Python3中不起作用。所以我最初的问题是“这是一个有效的想法”是错误的,这里有一些代码说明了Rawing的想法

class UselessObject(object):
    def __repr__(self):
        return "I'm useless"

class PretendClassDef(object):
    def do_something(self):
        return UselessObject()

# -------

class CoolObject(object):
    def __repr__(self):
        return "I'm cool"

UselessObject = CoolObject

p = PretendClassDef()
print(p.do_something())
输出

I'm cool

如果
CoolObject
需要继承
UselessObject
,我们甚至可以使用这种技术。如果我们将
CoolObject
的定义更改为:

class CoolObject(UselessObject):
    def __repr__(self):
        s = super().__repr__()
        return "I'm cool, but my parent says " + s
我们得到这个输出:

I'm cool, but my parent says I'm useless

这是因为在执行
CoolObject
类定义时,名称
UselessObject
有其旧定义

这不是元类的工作

相反,Python允许您通过一种称为“Monkeypatching”的技术来实现这一点,在运行时,您可以在运行时用一个对象替换另一个对象

在这种情况下,在调用
thirdyparty.invokeClassdef.do\u something

这样做的方法是一个简单的作业。 因此,假设您在问题中给出的示例片段是库中的trirdyparty模块,那么您的代码如下所示:

import thirdyparty

class CoolObject:
    # Your class definition here

thirdyparty.UselesObject = Coolobject
您必须注意的事项:以目标模块中使用的方式更改由
UselessObject
指向的对象

例如,如果您的伪ClassDef和UselessObject是在不同的模块中定义的,那么如果UselessObject是使用
从导入的,则您必须以一种方式进行处理。无用导入UselessObject
(在这种情况下,上面的示例很好),和
import.untible
,然后将其用作
untible.UselessObject
——在第二种情况下,您必须在
untible
模块上对其进行修补

此外,Python的
unittest.mock
有一个很好的可调用函数,如果出于某种原因希望修改在有限的范围内有效,例如在函数内或在带有
块的
内,可以正确地执行monkeypatching并撤销它。如果您不想在程序的其他部分更改第三方模块的行为,则可能会出现这种情况


至于元类,只有当您需要以这种方式更改要替换的类的元类时,元类才有任何用处,并且只有当您希望在继承自
UselessObject
的类中插入行为时,元类才有任何用处。在这种情况下,它将用于创建本地
CoolObject
,您仍然可以如上所述执行,但要注意在Python运行
UselessObject
的任何派生类的类体之前执行monkeypatching,在从第三方库进行任何导入时都要格外小心(如果这些子类是在同一个文件中定义的,那将是很棘手的)

这只是基于PM 2Ring和jsbueno的答案和更多的上下文:

如果您碰巧正在创建一个库供其他人用作第三方库(而不是使用第三方库),并且如果您需要CoolObject继承UselessObject以避免重复,以下内容可能有助于避免在某些情况下可能出现的无限递归错误:

模块1.py

class Parent:
    def __init__(self):
        print("I'm the parent.")

class Actor:
    def __init__(self, parent_class=None):
        if parent_class!=None: #This is in case you don't want it to actually literally be useless 100% of the time.
            global Parent
            Parent=parent_class
        Parent()
模块2.py

from module1 import *

class Child(Parent):
    def __init__(self):
        print("I'm the child.")

class LeadActor(Actor): #There's not necessarily a need to subclass Actor, but in the situation I'm thinking, it seems it would be a common thing.
    def __init__(self):
        Actor.__init__(self, parent_class=Child)

a=Actor(parent_class=Child) #prints "I'm the child." instead of "I'm the parent."
l=LeadActor() #prints "I'm the child." instead of "I'm the parent."

只是要小心,用户知道不要为具有不同Actor子类的parent_类设置不同的值。我的意思是,如果您创建多种类型的Actor,您只需要设置parent_类一次,除非您希望为所有类型的Actor都更改它。

为什么不导入该_库;该_库。UselessObject=MyOwnObject
?那么“我可能会有帮助。太好了!我不知道你能做到这一点!”Ashwini Chaudhary-Yep回答说,那篇帖子更好地描述了我的想法issue@Rawing,是的,这是一个好主意!给出的答案可以-但我们应该注意,这与“元类”无关-这被称为“Monkeypatching”。您确实需要添加
import other
other.UselessObject=CoolObject
,才能在OP中工作case@MarkusMeskanen没错,你可以从Rawing的评论中看到这一点,但这只是一个简单的MCVE,可以放在一个文件中。我不想在创建一个需要读者创建两个单独的示例时搞砸文件只是为了测试十几行代码。我理解你的想法,但事实是这样的,你应该直接回答OP的问题,在他的情况下,这不是他首先定义的类,而是其他人的包中的类。如果CoolObject需要继承UselessObject,这个答案就足够了吗?@Shule。当然!我刚刚添加了一个我编写了一些代码来说明这一点。谢谢!我从来都不太明白“猴子补丁”是什么。我将不得不查看
unittest.mock