【问题标题】:In Python, is referencing an instance variable from a staticmethod possible?在 Python 中,是否可以从静态方法中引用实例变量?
【发布时间】:2018-09-11 06:28:22
【问题描述】:

我知道以前有人问过这个问题,但我发现自己遇到了最适合使用静态方法的情况,但还需要在此类中引用实例变量。例如,假设我有以下课程:

class ExampleClass(object):
    def __init__(self, filename = 'defaultFilename'):
        self.file_name = filename

    @staticmethod
    def doSomethingWithFiles(file_2, file_1 = None):
         #if user didn't supply a file use the instance variable 
         if file_1 is None:
            # no idea how to handle the uninitialized class case to create
            # self.file_name. 
            file_1 = __class__.__init__().__dict__['file_name'] <--- this seems sketchy
         else:
            file_1 = file_1
         with open(file_1, 'r') as f1, open(file_2, 'w') as f2:
            .....you get the idea...

    def moreMethodsThatUseSelf(self):
        pass

现在假设我有几个带有不同文件名的 ExampleClass 实例(E1、E2、E3)传递到 __init__,但希望保留使用未初始化类 ExampleClass.doSomethingWithFiles(file_2 = E1.file_name, file_1) 的能力= E2.file_name) 或 E1.doSomethingWithFiles(file_2 = E2.file_name, file_1 = 'some_other_file') 视情况而定。

我有什么理由试图找到一种方法来做我在想的事情,还是我在搞砸?

更新 我认为 cmets 很有帮助,我也认为这是我遇到的一个问题,因为设计不好。

这个问题一开始是为了防止并发访问 HDF5 文件,方法是为每个类实例提供一个 rlock,我可以将其用作上下文管理器,以防止在文件正在使用时对其进行任何其他访问尝试。每个类实例都有它自己的 rlock,它在完成它需要做的任何事情后获取和释放。我还使用@staticmethod 来执行一个例程,然后生成一个文件,该文件被传递到它自己的 init() 中,并且对于每个类实例都是唯一的。当时觉得很聪明,但我后悔了。我还认为我完全不确定@staticmethods 何时合适,并且可能将它与@classmethods 混淆,但是类变量将不再使我的类实例独有的rlocks 和文件成为可能。我想我可能应该更多地考虑设计而不是试图用一个我并不真正理解的类定义来证明它是为了防止它被设计出来的。

【问题讨论】:

  • 你搞得一团糟。存在静态方法的唯一原因是不允许您访问实例。听起来你只是想要一个普通的方法。
  • 如果你得到答案,你应该接受它 - 如果它被 打开,如果其他人提出的问题与你的问题相似,它就不能用作建议的副本。
  • 它们被认为是不必要的。事实上,它们是 Python 的一个相对较晚的补充。据我所知,包含它是为了安抚 Java 开发人员。
  • 这是一个很好的使用静态方法的好例子(在标准库中多次看到):你有一个实例属性,它的值是一个函数,例如,self.callback。现在你想为它提供一个默认值。类属性可以用作实例属性的默认值——但 callback = lambda msg: None 在类级别定义了一个方法,而不是一个函数。 callback = staticmethod(lambda msg: None) 避免了这种情况,并按预期为您提供了一个简单的功能。 (请注意,重点是 not 是一个方法,因此 not 有一个self。)
  • @BrandonJ 糟糕的设计不是其他人的代码,而是我 6 个月或更长时间前的代码。我想重写它的方式当然是好的设计的定义——但仅限于前 6 个月。 :) 无论如何,如果你想了解更多关于类和静态方法的实际用途的信息,我有一篇几年前的不完整博客文章,我可以完成并链接到。但通过 grep 搜索标准库并查看核心开发人员如何使用它们可能更容易。

标签: python class variables instance static-methods


【解决方案1】:

如果您认为自己总是遇到最适合使用静态方法的情况,那么您可能错了——它们很少有好的用途。如果您的静态方法需要访问实例变量,那么您肯定错了。

静态方法不能直接访问实例变量。不能有该类的实例,也不能有数千个;您将从哪一个访问变量?


您要做的是创建一个新实例,只是为了访问它的实例变量。这有时会很有用——尽管这通常是一个好兆头,你一开始就不需要上课。 (而且,当它有用时,它通常值得发信号,让调用者写ExampleClass().doSomethingWithFiles而不是ExampleClass.doSomethingWithFiles。)

这是合法的,但您只需调用类即可,而不是调用其__init__ 方法。 __init__ 永远不会返回任何东西;它接收一个已经创建的self 并修改它。如果你真的想要,你可以调用它的__new__ 方法,但这实际上与调用类的意思是一样的。 (在它们不同的次要方面,它正在调用您想要的类。)

另外,一旦你有了一个实例,你就可以正常使用它;你不需要看它的__dict__。 (即使您只将属性名称作为字符串变量,getattr(obj, name) 几乎总是您想要的,而不是 obj.__dict__[name]。)

所以:

file_1 = __class__().file_name

那么,你应该怎么做呢?

好吧,看看你的设计。 ExampleClass 实例所做的唯一一件事就是保存一个文件名,该文件名具有默认值。为此,您不需要对象,只需传入一个普通的旧字符串变量,或者存储为全局变量。 (你可能听说过全局变量不好——但伪装的全局变量同样糟糕,而且还有一个额外的问题是它们是伪装的。这基本上就是你设计的。有时,全局变量是正确的回答。)

【讨论】:

  • 这是我的设计,随着时间的推移变得越来越难看。我添加了一个更新,希望能说明我是如何到达这里的。最初文件名是 hdf5store 句柄,其中一个实例变量是 rlock。当时我认为我很谨慎,因为我破坏了几个并发读/写的文件,但我没有想到在它们关联的类实例之外使用这些文件会有多困难。回到绘图板。
【解决方案2】:

为什么不输入实例作为静态方法的参数。我希望这段代码会有所帮助。

 class ClassA:
    def __init__(self, fname):
        self.fname = fname

    def print(self):
        print('fname=', self.fname)

    @staticmethod
    def check(f):
        if type(f)==ClassA :
            print('f is exist.')
            f.print()
            print('f.fname=', f.fname)
        else:
            print('f is not exist: new ClassA')
            newa = ClassA(f)
            return newa


a=ClassA('temp')
b=ClassA('test')
ClassA.check(a)
ClassA.check(b)
newa = ClassA.check('hello')
newa.print()

【讨论】:

  • 我需要坐下来多看看这个,因为我已经达到了当天代码的心理极限,但我真的很感谢你的解决方案。它实际上看起来可以解决我的一些问题,而无需我重做所有事情。当我更加神清气爽的时候,我一定会让你知道的。
【解决方案3】:

您不能从静态方法中引用实例属性。假设存在多个实例,您会从哪个实例中选择属性?

你似乎需要的是一个类属性和一个类方法。您可以使用classmethod 装饰器定义一个。

class ExampleClass(object):
    file_name = 'foo'

    @classmethod
    def doSomethingWithFiles(cls, file_2, file_1 = None):
        file_1 = cls.file_name
        # Do stuff

【讨论】:

  • 如果类变量是一个 rlock 并且类实例正在处理特定于类实例的数据文件,并且静态方法从其他文件生成此文件并将其作为唯一数据传递给自己,该怎么办?文件,而 rlock 应该防止其他类实例破坏特定类实例的数据文件,并且类实例偶尔会使用其他类实例的数据文件进行某些计算?我认为我正在摧毁自己的大脑并设计一些我不应该做的事情。
  • 从头开始可能是一个不错的选择。我不确定你到底需要什么,但似乎你需要重新考虑一个更好的架构。
  • @Olivier Melançon - 我认为你可能是对的,或者至少停止是个好主意。我做了一个特别糟糕的工作来解释这个案例,我应该尝试编码和呈现,而不是我原来问题中的示例。
【解决方案4】:

也许我误解了您的意图,但我认为您误用了默认参数。 您似乎正在尝试使用 'defaultFilename' 作为默认参数值。为什么不跳过尴尬

 if file_1 is None:
        # no idea how to handle the uninitialized class case to create
        # self.file_name. 
        file_1 = __class__.__init__().__dict__['file_name'] <--- this seems sketchy

并将函数更改如下,

def doSomethingWithFiles(file_2, file_1='defaultFilename'):

如果硬编码该值让您感到不舒服,不妨试试

class ExampleClass(object):
    DEFAULT_FILE_NAME = 'defaultFilename'

    def __init__(self, filename=DEFAULT_FILE_NAME):
        self.file_name = filename

    @staticmethod
    def doSomethingWithFiles(file_2, file_1=DEFAULT_FILE_NAME):
         with open(file_1, 'r') as f1, open(file_2, 'w') as f2:
            # do magic in here

    def moreMethodsThatUseSelf(self):
        pass

不过,一般来说,如果您想访问静态方法中的实例变量,您可能会错误地建模您的问题。

【讨论】:

  • 我提供了一个更新,可以更好地说明我是如何/为什么进入这个的。负责处理文件的类独有的 hdf5 存储句柄和 rlock 似乎是个好主意,但很快就失控了。我也不知道什么时候使用@staticmethod 并且应该假装它不存在,但这真的让我很困扰,因为我知道它在那里并且在真正需要它时可能会做一些非常有用的事情。我只是还没有找到它设计的模型案例
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-10
  • 2011-01-10
  • 1970-01-01
  • 1970-01-01
  • 2011-01-12
  • 1970-01-01
相关资源
最近更新 更多