亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

以最少侵入性和最隱蔽的方式檢測對象使用情況

以最少侵入性和最隱蔽的方式檢測對象使用情況

DIEA 2021-12-21 16:37:26
有沒有辦法自動檢測何時使用 python 對象(并可能對此做出反應)?例如,假設我有一個類型為 的對象Foo。我沒有為 編寫類代碼Foo,因為它來自外部庫。我想以這樣的方式“裝飾”我的對象,每當使用它的方法之一,或者每當它的內部狀態(成員)發生變化或被訪問時,我都會得到一些日志信息,比如"Foo is being used".我使用“裝飾”一詞來強調我不想更改Foo使用類型對象的所有接口。我只想為它添加一些功能。此外,我將避免Foo直接修改的類代碼,即通過print在其每個方法的開頭顯式添加一條語句(無論哪種方式都不會通知我其成員何時發生變化)。而且我不想將我的對象顯式注冊到其他一些對象,因為這將是一種“侵入性”方法,需要更改“客戶端”代碼(使用Foo對象的代碼),這將是這很容易被遺忘。
查看完整描述

3 回答

?
慕碼人2483693

TA貢獻1860條經驗 獲得超9個贊

我可以想到一個解決方案,它并不完美,但可能是一個開始。我們可以通過從裝飾類繼承的類中的__getattribute__和捕獲實例屬性訪問__setattribute__:


import re


dunder_pattern = re.compile("__.*__")

protected_pattern = re.compile("_.*")


def is_hidden(attr_name):

    return dunder_pattern.match(attr_name) or protected_pattern.match(attr_name)



def attach_proxy(function=None):

    function = function or (lambda *a: None)


    def decorator(decorated_class):


        class Proxy(decorated_class):

            def __init__(self, *args, **kwargs):

                function("init", args, kwargs)

                super().__init__(*args, **kwargs)


            def __getattribute__(self, name):

                if not is_hidden(name):

                    function("acces", name)

                return object.__getattribute__(self, name)


            def __getattr__(self, name):

                if not is_hidden(name):

                    function("acces*", name)

                return object.__getattr__(self, name)


            def __setattribute__(self, name, value):

                if not is_hidden(name):

                    function("set", name, value)

                return object.__setattribute__(self, name, value)


            def __setattr__(self, name, value):

                if not is_hidden(name):

                    function("set*", name, value)

                return object.__setattr__(self, name, value)


        return Proxy


    return decorator

然后你可以用它來裝飾你的班級:


@attach_proxy(print)

class A:

    x = 1

    def __init__(self, y, msg="hello"):

        self.y = y


    @classmethod

    def foo(cls):

        print(cls.x)


    def bar(self):

        print(self.y)

這將導致以下結果:


>>> a = A(10, msg="test")

init (10,) {'msg': 'test'}

set* y 10

>>> a.bar()

acces bar

acces y

10

>>> a.foo() # access to x is not captured

acces foo

1

>>> y = a.y

acces y

>>> x = A.x # access to x is not captured

>>> a.y = 3e5

set* y 300000.0

問題:


不會捕獲類屬性訪問(為此需要一個元類,但我看不到即時執行的方法)。


TypeA是隱藏的(在 type 后面Proxy),這可能更容易解決:


>>> A

__main__.attach_proxy.<locals>.decorator.<locals>.Proxy

另一方面,這不一定是問題,因為這會按預期工作:


>>> a = A(10, msg="test")

>>> isinstance(a, A)

True

編輯請注意,我不會將實例傳遞給function調用,但這實際上是一個好主意,將調用替換為function("acces", name)to function("acces", self, name)。這將允許與您的裝飾者一起制作更多有趣的東西。


查看完整回答
反對 回復 2021-12-21
?
Helenr

TA貢獻1780條經驗 獲得超4個贊

您可以使用猴子補丁來實現這一點。將對象上的成員函數之一重新分配為裝飾函數,該函數又調用原始函數,并添加了一些日志記錄。


例如:


a = Test() # An object you want to monitor

a.func() # A specific function of Test you want to decorate


# Your decorator

from functools import wraps

def addLogging(function):

    @wraps(function)

    def wrapper(*args, **kwargs):

        print 'Calling {}'.format(function.func_name)   

        return function(*args, **kwargs)

    return wrapper


a.func = addLogging(a.func)

但是請注意,猴子補丁最好僅用于單元測試,而不是生產代碼。它可能有不可預見的副作用,應謹慎使用。


至于識別成員變量的值何時發生變化,可以參考這個。


所有這些都需要您修改客戶端代碼——如果有一種方法可以在不改變客戶端代碼的情況下實現這一點,我不知道。


查看完整回答
反對 回復 2021-12-21
?
暮色呼如

TA貢獻1853條經驗 獲得超9個贊

您可以將@suicideteddy 提供的答案與方法檢查結合起來,結果類似于以下內容:


# Your decorator

def add_logging(function):

    @wraps(function)

    def wrapper(*args, **kwargs):

        print 'Calling {}'.format(function.func_name)   

        return function(*args, **kwargs)

    return wrapper


instance = Test() # An object you want to monitor


# list of callables found in instance

methods_list = [

   method_name for method_name in dir(instance) if callable(

      getattr(instance, method_name)

   )

]


# replaces original method by decorated one

for method_name in methods_list:

    setattr(instance, method_name, add_logging(getattr(instance, method_name))

我還沒有測試過這個,但類似的東西應該可以完成工作,祝你好運!


查看完整回答
反對 回復 2021-12-21
  • 3 回答
  • 0 關注
  • 172 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號