我对python类范围有点困惑。也关于它与线程的关系。以下是一个最小的工作示例。我创建了B的实例,并将其传递给A的实例。
我的理解方式A应该制作自己的B_instance本地副本。显然这没有发生,因为每次我以任何可以想象的方式修改B_instance的属性时,我都会看到A_instance和B_instance的变化(打印1 -6)。这是否意味着A_instance.other_class被认为是全局的?有没有办法使A_instance.other_class本地化?这样修改它就不会更改B_instance本身。
问题的第二部分与线程相关。我知道访问类属性不是线程安全的,因此我正在使用锁。但是,如果您看一下打印语句,我希望在“由主线程修改”之前先打印“锁定释放”。我想念什么?这两个锁似乎都在锁定其他东西,因为即使A_instance中的那个仍然有效,显然可以获取主线程中的锁。我觉得这与第一部分的发现相矛盾。
是否可以为整个对象而不是其属性或方法赋予锁定?
class A:
def __init__(self, other_class):
self.lock = threading.Lock()
self.other_class = other_class
def increment_B(self):
self.other_class.B_attr_1 += 1
def set_B(self):
self.other_class.B_attr_1 = 10
def thread_modify_attr(self):
self.lock.acquire()
self.other_class.B_attr_1 = "modified by class"
time.sleep(10)
self.lock.release()
print("lock released")
class B:
def __init__(self):
self.B_attr_1 = 0
if __name__ == "__main__":
B_instance = B()
A_instance = A(B_instance)
print("1:", B_instance.B_attr_1)
A_instance.other_class.B_attr_1 += 1
print("2:", B_instance.B_attr_1)
A_instance.other_class.B_attr_1 = 10
print("3:", B_instance.B_attr_1)
A_instance.increment_B()
print("4:", B_instance.B_attr_1)
A_instance.set_B()
print("5:", B_instance.B_attr_1)
B_instance.B_attr_1 = 0
print("6:", A_instance.other_class.B_attr_1)
lock = threading.Lock()
t = threading.Thread(target=A_instance.thread_modify_attr)
t.start()
print("thread started")
print(B_instance.B_attr_1)
lock.acquire()
B_instance.B_attr_1 = "modified by main thread"
lock.release()
print(B_instance.B_attr_1)
print("done")
t.join()
结果:
1: 0
2: 1
3: 10
4: 11
5: 10
6: 0
thread started
modified by class
modified by main thread
done
lock released
我知道有人在阅读python作用域详细信息的好地方,对此我将不胜感激。
答案 0 :(得分:0)
否。创建B的实例并将其传递给A构造函数时,将传递对同一B实例的 reference 。没有完成复制。如果需要,您必须自己制作一个副本。一种方法是使用copy.deepcopy
(尽管请注意,某些类型无法复制-threading.Lock
实例就是其中之一)。本质上,新引用(存储在A的self.other_class
中)是同一实例的另一个名称。
您的两个代码块能够同时执行的原因是您创建了两个不同的锁。您在类A构造函数中创建一个-由thread_modify_attr
锁定/解锁。另一个在子线程创建之前就在主线程代码中创建,并由主线程锁定和解锁。如果您希望两者使用相同的锁,则将该锁作为参数传递(引用)到线程函数中。因为它们不是同一个锁,所以没有什么阻止两个线程同时执行。
锁可以保护您想要保护的任何东西。确定锁所保护的内容是代码的责任。话虽这么说,您可以在类的构造函数中创建一个锁。然后,只要有任何引用该类实例的东西,就可以使用与该实例关联的锁来同步访问。一种常见的模式是在类中集中控制对象资源。这样外部代理便简单地要求对象做某事(通过调用其方法之一)。然后,对象在内部使用锁来保护其数据结构。
python作用域规则(在here中进行了描述
答案 1 :(得分:0)
只是@Gil上面答案的扩展
锁可以用作上下文管理器,因此您可以在with语句中使用它,并且由于任何原因(例如异常)退出with块时,锁会自动释放。
def thread_modify_attr(self):
with self.lock:
self.other_class.B_attr_1 = "modified by class"
time.sleep(10)
print("lock released")
注意::如果上面的链接失效了,只需直接转到the site,然后复制并粘贴此修改后的代码版本即可。唯一的变化是我删除了所有与线程相关的内容,因为该网站不支持。
class A:
def __init__(self, other_class):
self.other_class = other_class
def increment_B(self):
self.other_class.B_attr_1 += 1
def set_B(self):
self.other_class.B_attr_1 = 10
class B:
def __init__(self):
self.B_attr_1 = 0
if __name__ == "__main__":
B_instance = B()
A_instance = A(B_instance)
print("1:", B_instance.B_attr_1)
A_instance.other_class.B_attr_1 += 1
print("2:", B_instance.B_attr_1)
A_instance.other_class.B_attr_1 = 10
print("3:", B_instance.B_attr_1)
A_instance.increment_B()
print("4:", B_instance.B_attr_1)
A_instance.set_B()
print("5:", B_instance.B_attr_1)
B_instance.B_attr_1 = 0
print("6:", A_instance.other_class.B_attr_1)
print(B_instance.B_attr_1)
print("done")