簡體   English   中英

一種從 Python 中的類實例訪問上下文管理器變量的方法?

[英]A way to access context manager variable from class instance in Python?

我會從其他模塊中定義的某些類訪問上下文中定義的一些變量,就像我在以下內容中進行了超級簡化:

在 main.py 中

import class_def1
import class_def2

class Starter(object):
    #context manager
    def __init__(self):
        self.att = 'Myfirst'

    def __enter__(self):
        return self.att

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

with Starter() as f:
    for name in ('a', 'b', 'c'):
        for number in range(2):
            c1 = class_def1.MyClass1()
            print(c1.attr1)
            c2 = class_def2.MyClass2()
            print(c2.attr2)

而在 class_def1.py 中:

class MyClass1(object):
    global f
    global number
    def __init__(self):
        self.attr1 = ','.join([f,str(number)])

在 class_def2.py 中:

class MyClass2(object):
    def __init__(self):
        global f
        global name
        global number
        self.attr2 = ','.join([f,name,str(number)])

這段代碼不起作用,我還想避免同時使用“全局”和init輸入參數,因為我有很多要“傳遞”的變量......也許我要求太多了。 有什么建議嗎?

新的考慮

考慮到使用數據類作為 DTO 的可能性(如@Niel Godfrey Ponciano 所建議的),我可以通過這種方式創建一個數據類(或者甚至可能列出其他變量並將默認值設置為零):

@dataclass
class AllInfo:
      f: str

with Starter() as f:
    info = AllInfo(f)
    for name in (a,b,c):
        info.name = name
        for number in range(2):
            info.number = number
            c1 = class_def1.MyClass1(info)
            print(c1.attr1)
            c2 = class_def2.MyClass2(info)
            print(c2.attr2)

但是,由於它只是存儲變量的問題(不需要任何方法),因此這等效於:

class AllInfo:
      def __init__(self,f):
        self.f = f

(在我看來,自動添加到數據類中的repr方法無關緊要)。

但是,在這兩種情況下,我都需要傳遞整個信息集。 可能不是這樣的問題,因為它是一個指針......

關於上下文管理器Starter__enter__方法,如文檔中所述:

此方法返回的值綁定到使用此上下文管理器的 with 語句的 as 子句中的標識符。

返回的值是self.att

def __enter__(self):
    return self.att  # Returns the string "Myfirst"

這意味着f的值將是字符串“Myfirst”。 如果您想要f是整個Starter實例,而不僅僅是屬性Starter.att ,那么請return self而不是return self.att

with Starter() as f:  # f will be a string "Myfirst"

現在,下一步是將該上下文管理器變量(來自Starter.att f )以及一些迭代值namenumber傳遞給您的其他類MyClass1MyClass2 執行此操作的一種標准方法是執行依賴項注入,其中我們將依賴項fnamevalue注入目標客戶端,即MyClass1MyClass2 簡單來說,設計是MyClass1MyClass2依賴於fnamevalue

解決方案1:使用構造函數注入

你提到你不想傳遞給init。 但這是執行此操作的標准方法之一,也是最簡單的方法。 如果它更適合您,請繼續下面的解決方案 2,

class Starter(object):
    #context manager
    def __init__(self):
        self.att = 'Myfirst'

    def __enter__(self):
        return self.att

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass


class MyClass2(object):
    def __init__(self, f, name, number):
        text_list = [f, name, str(number)]
        self.attr2 = ','.join(text_list)


with Starter() as f:
    for name in ('a', 'b', 'c'):
        for number in range(2):
            # Inject all dependencies to the class
            c2 = MyClass2(f, name, number)
            print(c2.attr2)

輸出:

Myfirst,a,0
Myfirst,a,1
Myfirst,b,0
Myfirst,b,1
Myfirst,c,0
Myfirst,c,1

解決方案2:使用setter注入

如果您有很多變量,但只會使用其中的少數幾個,這可能適用。 最好的用例之一是構建器設計模式

class Starter(object):
    #context manager
    def __init__(self):
        self.att = 'Myfirst'

    def __enter__(self):
        return self.att

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass


class MyClass2(object):
    def __init__(self):
        self._f = None
        self._name = None
        self._number = None

    # Here, we are using property getters and setters. Of course you can also do it by just directly
    # accessing the variable from the outside, just remove the _ in the name to imply that they are
    # public and not private as it is now).
    @property
    def f(self):
        return self._f

    @f.setter
    def f(self, value):
        self._f = value

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

    @property
    def number(self):
        return self._number

    @number.setter
    def number(self, value):
        self._number = value

    @property
    def attr2(self):
        # Optionally, you can add checks to filter out null variables in the list
        text_list = [self.f, self.name, str(self.number)]
        return ','.join(text_list)


with Starter() as f:
    for name in ('a', 'b', 'c'):
        for number in range(2):
            c2 = MyClass2()
            # Manually inject each dependency to the class. You can add conditionals to select what
            # should be injected and what shouldn't.
            c2.f = f
            c2.name = name
            c2.number = number
            print(c2.attr2)

輸出:

  • 與解決方案 1 相同

如果您有很多變量,如@chepner 所建議的,您可以將它們全部(或按相關數據組)放入其自己的數據結構中。 您可以為此目的使用數據類(就像 C/C++ 中的struct一樣),它將充當您的數據傳輸對象 (DTO)

Python 旨在允許在不使用全局聲明的情況下讀取全局變量,如果函數需要修改在全局范圍內聲明的變量,則需要使用全局聲明。 所以下面的代碼應該工作

class MyClass1(object):
    def __init__(self):
        self.attr1 = ','.join([f,str(number)])

class MyClass2(object):
    def __init__(self):
        self.attr2 = ','.join([f,name,str(number)])

class Starter(object):
    #context manager
    def __init__(self):
        self.att = 'Myfirst'

    def __enter__(self):
        return self.att

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

with Starter() as f:
    for name in ('a', 'b', 'c'):
        for number in range(2):
            c1 = MyClass1()
            print(c1.attr1)
            c2 = MyClass2()
            print(c2.attr2)

>>> Myfirst,0
    Myfirst,a,0
    Myfirst,1
    Myfirst,a,1
    Myfirst,0
    Myfirst,b,0
    Myfirst,1
    Myfirst,b,1
    Myfirst,0
    Myfirst,c,0
    Myfirst,1
    Myfirst,c,1

如果這沒有幫助,並且您確實想訪問在另一個類的上下文中定義的某個變量,則需要在初始化時傳遞該變量。 像這樣的東西:

class MyClass1(object):
    def __init__(self,f):
        self.attr1 = ','.join([f,str(number)])

class MyClass2(object):
    def __init__(self,f):
        self.attr2 = ','.join([f,name,str(number)])

class Starter(object):
    #context manager
    def __init__(self):
        self.att = 'Myfirst'

    def __enter__(self):
        return self.att

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

with Starter() as f:
    for name in ('a', 'b', 'c'):
        for number in range(2):
            c1 = MyClass1(f)
            print(c1.attr1)
            c2 = MyClass2(f)
            print(c2.attr2)

暫無
暫無

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

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