我正在为一些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没有内置的
- 导入成功,因此Python有一个
module
对象,并将其绑定到当前模块范围(client
(中的user
名称 - 继续运行该文件,会遇到一个类定义(定义了一个方法(,并将其绑定到当前模块中的
Client
名称 - 则它实例化
Client
并将其绑定到当前模块中的client
名称(也命名为client
( - 然后它调用
authenticate
,它将user
(指向从user.py
加载的模块的变量的名称(.User
(模块中的类(._client
(类中的静态变量(设置为self
- 最后它打印出静态变量new值实际上是
Client
的一个实例
在您的问题评论中,建议使用global
变量。它不会改变任何东西,因为User
类本质上已经是单例(只有一个声明(,您只是碰巧访问了它的一个字段。singleton的字段也可以被视为singleton值。
我不知道你的实现有什么问题,你没有给我们足够的信息来找到错误。如果你想找到它,我建议你试着把它简化为一个最小可复制的例子。通常在这个过程中,你会自己找到解决方案。
我担心这可能是由相互导入引起的,因此变量的状态取决于项目的每个文件中导入语句的顺序。这是一个尝试打破循环的理由,或者在循环周围要小心。
我认为您可能对如何在search.py和client.py中导入类User有问题。如果您使用不同的路径在这些文件中导入User模块,则它们将作为不同的类名加载(因此加载两次(。当每次加载都执行User类代码时,您可能会将_client初始化两次为None。你可以在这个帖子中找到这个问题的更多细节
另一个可能的原因是您的代码可能显式地进行导入和模块重载调用。一些动态加载模块的框架倾向于进行此类调用来加载模块。强制重新加载User类将导致_client设置为None