簡體   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