对静态类变量的更改在其他模块中不可见



我正在为一些REST API编写一个Python包。为了更容易地操作从API获得的对象的状态,我希望存储对API客户端实例的静态引用。假设我有一个文件user.py:

class User:
_client: Client = None

因此,当客户端第一次成功验证时,我更新了参考:

class Client:
def authenticate(self):
...
User._client = self

这种情况发生在我的client.py文件中。现在,我可以从client.py中声明的任何函数成功访问User._client,但当我从另一个模块尝试相同的操作时,User._client仍然是None

假设这是我的文件夹结构

└── my_api_client
├── __init__.py
├── client.py
├── search.py
└── user.py

然后,当我从search.py中的函数调用User._client时,我得到的是None,而不是对当前客户端实例的引用。

我在创建一个演示这个问题的最小工作示例时遇到了问题,所以也许我只是把导入的东西搞砸了。如果我能更好地理解如果我从另一个模块导入一个类,以及如果对该类的静态属性的引用在所有模块之间共享,会发生什么,我认为这将非常有帮助。如果真的是这样,我做错了什么?

我试图重现您的问题,但无法。

# user.py
print("defining class User")

class User:
_client = None  # removed type annotation here to prevent circular import

print("right now, User._client is " + repr(User._client))
# client.py
import user

class Client:
def authenticate(self):
user.User._client = self

print("instantiating a Client")
client = Client()
print("before athenticating, the User._client is " + repr(so70744003_user.User._client))
client.authenticate()
print("before athenticating, the User._client is " + repr(so70744003_user.User._client))

并运行文件client.py:

defining class User
right now, User._client is None
instantiating a Client
before athenticating, the User._client is None
before athenticating, the User._client is <__main__.Client object at 0x7fc1816a9b20>

我可以给你一个发生的事情的详细分类:

  • Python获得了文件client.py,因此开始运行该文件中的代码
  • 它从import user开始,因此Python将搜索user模块并加载它
    • Python没有内置的user模块,因此它在sys.path目录中搜索它,并找到匹配的文件
    • Python开始运行user.py文件,该文件定义了一个User类,因此它为其实例化了class实例(静态成员_client设置为None(,并将其绑定到当前模块中的User名称
    • 则它打印出User的静态成员_client确实是None
    • 运行完user.py文件后,Python将继续运行client.py文件
  • 导入成功,因此Python有一个module对象,并将其绑定到当前模块范围(client(中的user名称
  • 继续运行该文件,会遇到一个类定义(定义了一个方法(,并将其绑定到当前模块中的Client名称
  • 则它实例化Client并将其绑定到当前模块中的client名称(也命名为client(
  • 然后它调用authenticate,它将user(指向从user.py加载的模块的变量的名称(.User(模块中的类(._client(类中的静态变量(设置为self
  • 最后它打印出静态变量new值实际上是Client的一个实例

在您的问题评论中,建议使用global变量。它不会改变任何东西,因为User类本质上已经是单例(只有一个声明(,您只是碰巧访问了它的一个字段。singleton的字段也可以被视为singleton值。

我不知道你的实现有什么问题,你没有给我们足够的信息来找到错误。如果你想找到它,我建议你试着把它简化为一个最小可复制的例子。通常在这个过程中,你会自己找到解决方案。

我担心这可能是由相互导入引起的,因此变量的状态取决于项目的每个文件中导入语句的顺序。这是一个尝试打破循环的理由,或者在循环周围要小心。

我认为您可能对如何在search.pyclient.py中导入类User有问题。如果您使用不同的路径在这些文件中导入User模块,则它们将作为不同的类名加载(因此加载两次(。当每次加载都执行User类代码时,您可能会将_client初始化两次为None。你可以在这个帖子中找到这个问题的更多细节

另一个可能的原因是您的代码可能显式地进行导入和模块重载调用。一些动态加载模块的框架倾向于进行此类调用来加载模块。强制重新加载User类将导致_client设置为None

最新更新