繁体   English   中英

使用元类来替换类定义?

[英]Using a metaclass to substitute a class definition?

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的创建UselessObject是一个有效的想法?

编辑

这个答案张贴阿什维尼·乔杜里对同一个问题,可能是使用他人的。 以及以下答案。

PS我还发现'模块'级别__metaclass__在python 3中不起作用。所以我最初的问题'它是一个有效的想法'是假的

FWIW,这里有一些代码说明了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的定义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.UselessObjectyour.CoolObject之前调用thirdyparty.PretendClassDef.do_something

这样做的方法是一个简单的任务。 因此,假设您在问题上提供的示例代码段是trirdyparty模块,在库中,您的代码将如下所示:

import thirdyparty

class CoolObject:
    # Your class definition here

thirdyparty.UselesObject = Coolobject

您需要注意的事项:您在目标模块中使用的方式更改UselessObject指向的对象。

例如,你的PretendedClassDef和Usel​​essObject是在不同的模块中定义的,如果使用from .useless import UselessObject ,则必须以一种方式处理from .useless import UselessObject (在这种情况下上面的示例很好),并import .useless以后再使用它as useless.UselessObject - 在第二种情况下,你必须在useless模块上修补它。

此外,Python的unittest.mock有一个很好的patch可调用,可以正确执行monkeypatching并撤消它,如果由于某种原因你希望修改在有限的范围内有效,比如在你的函数内,或在with块内。 如果您不想在程序的其他部分中更改thirdyparty模块的行为,则可能就是这种情况。

至于元类,如果你需要以这种方式改变你要替换的类的元类,它们只会有任何用处 - 如果你想在类中插入行为,它们只能有用。继承自UselessObject 在这种情况下,它将用于创建本地CoolObject ,你仍然可以执行上述操作,但是 Python运行任何派生类的UselessObject的类主体之前要注意你执行UselessObject ,要特别小心从第三方库中进行任何导入时(如果在同一文件中定义了这些子类,那将会非常棘手)

这只是建立在PM 2Ring和jsbueno的答案上的更多背景:

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

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

module2.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_class的不同值。 我的意思是,如果你制作多种Actors,你只需要设置一次parent_class,除非你希望它为所有这些都改变。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM