簡體   English   中英

python-2.7執行對象導入代碼

[英]python-2.7 execute code on object import

我正在嘗試建立一個允許代碼以透明方式為常規用戶運行的棄用系統,但在開發人員模式下標記已棄用的對象。

我遇到的一個問題是,即使我處於開發人員模式,我也可以將已棄用的對象導入另一個模塊。 這意味着我缺少使用棄用對象的地方。

例如在module1.py中:

class MyObject(object):
    pass
MyObject = MyObject if not dev_mode() else DeprecatedObject

然后在module2.py中:

from module1 import MyObject

我已經設置了DeprecatedObject,以便與它進行任何交互都會引發DeprecationWarning - 有什么辦法可以讓我在導入時出錯? 即。 甚至導入module2.py都會引發異常。

我想象的是:

import warnings

class DeprecatedObject(object):
    ...
    def __onimport__(self):
        warnings.warn("deprecated", DeprecationWarning)

除了其他方面,模塊級別__getattr__功能允許模塊級別名稱在導入時進行正確的棄用過程。 這個功能將在Python 3.7中出現,有關詳細信息,請參閱PEP 562 (因為您已經使用Python 2.7標記,它無法幫助您,但我為了未來讀者的利益而提及它)。

在Python 2.7上,您有兩個較差的選項:

  • 在對象__init__觸發棄用警告。
  • 導入后使用Guido的hack用自身的修補版本替換模塊。 在模塊周圍包裝代理對象允許您控制名稱解析。

首先,我建議查看內置warnings模塊。 它有專門為這類東西制作的工具。 發出非致命警告比提出異常更有意義。

現在,對於您的情況,一種可能的做法是用函數“替換”已棄用的類。 這意味着將類重命名為其他類,並具有原始名稱的函數,該函數檢查開發者模式是否已啟用並相應地執行。 結果將是這樣的:

class MyDeprecatedClass:
    pass

def MyClass(*args, **kwargs):
    if dev_mode():
        raise DeprecationWarning
    else:
        return MyDeprecatedClass(*args, **kwargs)

或者,有警告:

def MyClass(*args, **kwargs):
    from warnings import warn
    if dev_mode():
        warn("Dont use this!!!!!!!!!")
    else:
        return MyDeprecatedClass(*args, **kwargs)

這樣做是檢查是否啟用了開發人員模式,並且只提出異常(或警告)。 否則,它將給予它的所有參數傳遞給重命名類的構造函數,這意味着所有依賴它的舊函數都可以正常工作。

你的初始方法幾乎就是我所建議的,除了你允許兩種類型的對象同時存在。 我將從您的模塊中的一個完整的if語句開始,它只允許一次定義一個對象。 更像是:

if dev_mode():
    class MyObject:
        # Define deprecated version here
        ...
else:
    class MyObject:
        # Define production version here
        ...

如果棄用版本和非棄用版本之間的差異很簡單,例如,可以通過函數或類裝飾器輕松完成(如提出警告),則可以將上面的代碼簡化為:

if dev_mode():
    def function_decorator(func, cls=None):
        # You can use the second argument when calling manually from a class decorator
        name = func.__name__ is cls is None else cls.__name__ + '.' + func.__name__
        warnings.warn("Importing deprecated function: {}".format(name))
        return func

    def class_decorator(cls):
        warnings.warn("Importing deprecated class: {}".format(cls.__name__))
        # Make additional modifications here (like adding function_decorator to all the class methods)
        return cls
else:
    def function_decorator(func):
        return func
    def class_decorator(cls):
        return cls

@class_decorator
class MyClass:
    pass

使用模塊級if為了避免浮動類的多個版本是這里的基本工具。 您可以為流程添加任意數量的復雜層。 我看到的一個類似目的的技術(其中特定版本的類依賴於某些導入時間條件,如OS),是創建一個名為module1的包,並在不同的模塊中完全實現兩個不同版本的類。 包結構如下所示:

module1/
|
+-- __init__.py
|
+-- _development.py
|
+-- _production.py

_development_production定義了相同的名稱,但版本不同。 模塊名稱前面的下划線表示永遠不應該直接導入它們。 您將module1公開為模塊而不是使用其__init__文件作為包,這看起來像這樣:

__all__ = ['MyModule']

if dev_mode():
    from ._development import MyModule
else:
    from ._production import MyModule

如果您有很多名稱,可以使用__init__ __all__自動執行公共導入:

import importlib, sys

__all__ = ['MyClass']

self = sys.modules[__name__]
sub = importlib.import_module('_development' if dev_mode() else '_production')
for name in __all__:
    setattr(self, name, getattr(sub, name))

這種分離形式允許您在沒有兩個單獨的測試流程的情況下測試生產版本和開發版本。 您的測試可以直接導入私有模塊。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM