警告:以下問題是要求提供有關不良實踐和髒代碼的信息。建議開發人員酌情決定。如何在Python中創建第二個None?創建一個id始終相同的單例對象
注意:這與Creating a singleton in Python問題不同,因爲我們想要解決酸洗和複製以及正常的對象創建問題。
目標:我想創建一個模擬None
行爲的值(稱爲NoParam
)。具體而言,我希望NoParamType
的任何實例具有相同的值,即具有相同的值id
---以便is
運算符始終在這些值中的兩個值之間返回True
。
爲什麼:我有保存參數值的配置類。其中一些參數可以採用None
作爲有效類型。但是,如果沒有指定類型,我希望這些參數採用特定的默認值。因此我需要一些哨兵is not None
來指定沒有指定參數。這個標記必須是 永遠不能用作有效參數值的東西。我寧願有一個特殊類型的哨兵,而不是使用一些不太可能被使用的字符串。
例如:
def add_param(name, value=NoParam):
if value is NoParam:
# do some something
else:
# do something else
但是讓我們不要太在乎的原因。讓我們關注如何。
我有什麼至今:
我能實現大部分的這種行爲很容易地。我創建了一個名爲util_const.py
的特殊模塊。它包含一個創建NoParamType然後創建該類的單例實例的類。
class _NoParamType(object):
def __init__(self):
pass
NoParam = _NoParamType()
我只是假設這個類的第二個實例永遠不會被創建。無論何時我想使用值I import util_const
並使用util_const.NoParam
。
這適用於大多數情況。但是,我剛剛遇到一個案例,其中 a NoParam
值被設置爲對象值。該對象使用copy.deepcopy
進行了深度複製,因此創建了第二個NoParam實例。
我發現這是一個非常簡單的解決方法通過定義__copy__
和__deepcopy__
方法
class _NoParamType(object):
def __init__(self):
pass
def __copy__(self):
return NoParam
def __deepcopy__(self, memo):
return NoParam
NoParam = _NoParamType()
現在,如果deepcopy
是不斷呼籲沒有NoParam
它簡單地返回現有NoParam
實例。
現在的問題:
有什麼我可以做,以實現與酸洗此相同的行爲?最初我以爲我可以定義__getstate__
,但第二個實例已經在那個時候創建。基本上我想pickle.loads(pickle.dumps(NoParam)) is NoParam
返回True。有沒有辦法做到這一點(也許與元類)?
爲了更進一步:有什麼我可以做的,以確保只有一個NoParam實例創建?
解決方案
非常感謝@ user2357112回答有關酸洗的問題。 我也想出瞭如何使這個類健壯的模塊重新加載以及。下面是我學到的東西都放在一起
# -*- coding: utf-8 -*-
# util_const.py
class _NoParamType(object):
"""
Class used to define `NoParam`, a setinal that acts like None when None
might be a valid value. The value of `NoParam` is robust to reloading,
pickling, and copying.
Example:
>>> import util_const
>>> from util_const import _NoParamType, NoParam
>>> from six.moves import cPickle as pickle
>>> import copy
>>> versions = {
... 'util_const.NoParam': util_const.NoParam,
... 'NoParam': NoParam,
... '_NoParamType()': _NoParamType(),
... 'copy': copy.copy(NoParam),
... 'deepcopy': copy.deepcopy(NoParam),
... 'pickle': pickle.loads(pickle.dumps(NoParam))
... }
>>> print(versions)
>>> assert all(id(v) == id_ for v in versions.values())
>>> import imp
>>> imp.reload(util_const)
>>> assert id(NoParam) == id(util_const.NoParam)
"""
def __new__(cls):
return NoParam
def __reduce__(self):
return (_NoParamType,())
def __copy__(self):
return NoParam
def __deepcopy__(self, memo):
return NoParam
def __call__(self, default):
pass
# Create the only instance of _NoParamType that should ever exist
# When the module is first loaded, globals() will not contain NoParam. A
# NameError will be thrown, causing the first instance of NoParam to be
# instanciated.
# If the module is reloaded (via imp.reload), globals() will contain
# NoParam. This skips the code that would instantiate a second object
# Note: it is possible to hack around this via
# >>> del util_const.NoParam
# >>> imp.reload(util_const)
try:
NoParam
except NameError:
NoParam = object.__new__(_NoParamType)
[在Python中創建單例]的可能副本(http://stackoverflow.com/questions/6760685/creating-a-singleton-in-python) – lxop
@lxop:這些單例不是singleton-y就足夠了。 (真的,他們不是。) – user2357112
誠然,他們不處理酸洗方面的問題。 – lxop