これが尋ねられてから1か月が経過したことを認識していますが、同様の問題が発生し(つまりpow(float('nan'), 1)
、Jython 2.52b2などの一部のPython実装で例外がスローされます)、上記の回答が私とはまったく異なることがわかりました。探している。
6502で提案されているMissingDataタイプを使用するのは道のりのようですが、具体的な例が必要でした。Ethan FurmanのNullTypeクラスを試しましたが、データ型を強制しないため、これは算術演算では機能しないことがわかりました(以下を参照)。また、オーバーライドされた各算術関数に明示的に名前を付けることも好きではありませんでした。
イーサンの例とここで見つけたコードの微調整から始めて、私は以下のクラスに到着しました。このクラスには多くのコメントがありますが、実際には関数型コードが数行しか含まれていないことがわかります。
重要なポイントは次のとおり
です。1。coerce()を使用して、混合型(NoData + floatなど)の算術演算用に2つのNoDataオブジェクトを返し、文字列ベース(concatなど)の演算用に2つの文字列を返します。
2. getattr()を使用して、他のすべての属性/メソッドアクセス用に呼び出し可能なNoData()オブジェクトを返します。3。call()を
使用して、NoData()オブジェクトの他のすべてのメソッドを実装します。NoData()オブジェクトを返します。
これがその使用例です。
>>> nd = NoData()
>>> nd + 5
NoData()
>>> pow(nd, 1)
NoData()
>>> math.pow(NoData(), 1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: nb_float should return float object
>>> nd > 5
NoData()
>>> if nd > 5:
... print "Yes"
... else:
... print "No"
...
No
>>> "The answer is " + nd
'The answer is NoData()'
>>> "The answer is %f" % (nd)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: float argument required, not instance
>>> "The answer is %s" % (nd)
'The answer is '
>>> nd.f = 5
>>> nd.f
NoData()
>>> nd.f()
NoData()
NoData()でpowを使用すると**演算子が呼び出されるため、NoDataで機能することに気付きましたが、math.powを使用すると、最初にNoData()オブジェクトをfloatに変換しようとするため機能しません。私は非数学の捕虜を使って満足しています-うまくいけば、6502などが上記のコメントで捕虜に問題があったときにmath.powを使っていました。
私が解決する方法を考えることができないもう1つの問題は、format(%f)演算子を使用することです...この場合、NoDataのメソッドは呼び出されません。フロートを指定しないと、演算子は失敗します。とにかくここにクラス自体があります。
class NoData():
"""NoData object - any interaction returns NoData()"""
def __str__(self):
#I want '' returned as it represents no data in my output (e.g. csv) files
return ''
def __unicode__(self):
return ''
def __repr__(self):
return 'NoData()'
def __coerce__(self, other_object):
if isinstance(other_object, str) or isinstance(other_object, unicode):
#Return string objects when coerced with another string object.
#This ensures that e.g. concatenation operations produce strings.
return repr(self), other_object
else:
#Otherwise return two NoData objects - these will then be passed to the appropriate
#operator method for NoData, which should then return a NoData object
return self, self
def __nonzero__(self):
#__nonzero__ is the operation that is called whenever, e.g. "if NoData:" occurs
#i.e. as all operations involving NoData return NoData, whenever a
#NoData object propagates to a test in branch statement.
return False
def __hash__(self):
#prevent NoData() from being used as a key for a dict or used in a set
raise TypeError("Unhashable type: " + self.repr())
def __setattr__(self, name, value):
#This is overridden to prevent any attributes from being created on NoData when e.g. "NoData().f = x" is called
return None
def __call__(self, *args, **kwargs):
#if a NoData object is called (i.e. used as a method), return a NoData object
return self
def __getattr__(self,name):
#For all other attribute accesses or method accesses, return a NoData object.
#Remember that the NoData object can be called (__call__), so if a method is called,
#a NoData object is first returned and then called. This works for operators,
#so e.g. NoData() + 5 will:
# - call NoData().__coerce__, which returns a (NoData, NoData) tuple
# - call __getattr__, which returns a NoData object
# - call the returned NoData object with args (self, NoData)
# - this call (i.e. __call__) returns a NoData object
#For attribute accesses NoData will be returned, and that's it.
#print name #(uncomment this line for debugging purposes i.e. to see that attribute was accessed/method was called)
return self