2 回答

TA貢獻1798條經驗 獲得超7個贊
這樣的問題可能存在爭議。但我通常會說是的,這是“不好的做法”,因為變革的范圍和影響確實變得模糊。請注意,您描述的用例實際上不是關于共享配置,而是關于程序函數、對象、模塊交換數據的不同部分,因此它是(元)變量的一些變體global。
讀取通用配置值可能沒問題,但一路更改它們...您可能會忘記在導入模塊/修改值時發生的情況以及順序。例如假設config.py和 兩個模塊m1.py:
import config
print(config.x)
config.x=1
和m2.py:
import config
print(config.x)
config.x=2
和 amain.py就這樣做:
import m1
import m2
import config
print(config.x)
或者:
import m2
import m1
import config
print(config.x)
您在每個模塊以及其他任何模塊(包括main.py此處)中找到配置的狀態取決于導入發生的順序以及誰在何時分配什么值。即使對于完全在您控制之下的程序,這也可能很快變得令人困惑(以及錯誤的根源)。
對于運行時數據以及在對象和模塊之間傳遞信息(您的示例確實是這樣,而不是在模塊之間預定義和共享的配置),我建議您考慮在自定義狀態(配置)對象中描述信息并將其傳遞通過適當的接口。但實際上可能只需要一個函數/方法參數即可。確切的形式取決于您到底想要實現什么以及您的整體設計是什么。
在您的示例中,other.py之前調用或導入時的行為有所不同top.py,在最小的示例中可能仍然看起來很明顯且易于管理,但實際上并不是一個非常合理的設計。任何閱讀代碼的人(包括未來的你)都應該能夠遵循其邏輯,而這個 IMO 打破了它的流程。
對于您所描述的內容,最簡單(也是程序性的)示例,現在我希望能夠更好地理解,那就是重新other.py創建您當前的行為:
def do_stuff(value):
print(value) # We did something useful here
if __name__ == "__main__":
do_stuff(None) # Could also use config with defaults
您top.py可能是入口點并協調導入和執行:
import other
x = get_the_value()
other.do_stuff(x)
您當然可以引入一個接口來配置do_stuff一個dict或一個自定義類,即使使用默認實現config.py:
class Params:
def __init__(self, x=None):
self.x = x
和你的other.py:
def do_stuff(params=config.Params()):
print(params.x) # We did something useful here
在你的上top.py你可以使用:
params = config.Params(get_the_value())
other.do_stuff(params)
但您也可以擁有任何特定于用例的值源:
class TopParams:
def __init__(self, url):
self.x = get_value_from_url(url)
params = TopParams("https://example.com/value-source")
other.do_stuff(params)
x甚至可以是property您每次訪問它時檢索的內容...或者在需要時懶惰地檢索然后緩存...同樣,這實際上是您需要做什么的問題。

TA貢獻1828條經驗 獲得超6個贊
“從一個模塊修改另一個模塊的屬性是一種不好的做法嗎?”
這被認為是不好的做法——違反了德米特法則,這實際上意味著“與朋友交談,而不是與陌生人交談”。
對象應該公開行為和功能,但應該隱藏數據。DataStructures 應該公開數據,但不應該有任何方法(公開的)。德米特定律不適用于此類數據結構。OOP 純粹主義者可能會使用 setter 和 getter 來覆蓋此類 DataStructures,但它實際上在 Python 中沒有增加任何價值。
我保留配置的首選方法是(此處不使用 attrs):
# conf_xy.py
"""
config is code - so why use damned parsers, textfiles, xml, yaml, toml and all that
if You just can use testable code as config that can deliver the correct types, etc.
as well as hinting in Your favorite IDE ?
Here, for demonstration without using attrs package - usually I use attrs (read the docs)
"""
class ConfXY(object):
? ??
? ? def __init__(self) -> None:
? ? ? ? self.x: int = 1
? ? ? ? self.z: float = get_z_from_input()
? ? ? ? ...
conf_xy=ConfXY()
# other.py
from conf_xy import conf_xy
...
y = conf_xy.x * 2
...?
添加回答
舉報