繁体   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