引数の型に基づいて__init__メソッドをオーバーロードする方法は?
-
02-07-2019 - |
質問
リストと呼ばれるdataというメンバーを持つクラスがあるとします。
たとえば、ファイル名(リストを初期化するデータを含む)または実際のリストを使用して、クラスを初期化できるようにします。
これを行うためのテクニックは何ですか?
__ class __
を見て、型を確認するだけですか?
見逃している可能性のあるトリックはありますか?
私は、引数の型によるオーバーロードが簡単なC ++に慣れています。
解決
「代替コンストラクター」を取得するためのより洗練された方法は、クラスメソッドを使用することです。例えば:
>>> class MyData:
... def __init__(self, data):
... "Initialize MyData from a sequence"
... self.data = data
...
... @classmethod
... def fromfilename(cls, filename):
... "Initialize MyData from a file"
... data = open(filename).readlines()
... return cls(data)
...
... @classmethod
... def fromdict(cls, datadict):
... "Initialize MyData from a dict's items"
... return cls(datadict.items())
...
>>> MyData([1, 2, 3]).data
[1, 2, 3]
>>> MyData.fromfilename("/tmp/foobar").data
['foo\n', 'bar\n', 'baz\n']
>>> MyData.fromdict({"spam": "ham"}).data
[('spam', 'ham')]
見栄えがよいのは、どのタイプが期待されるかについて疑いがなく、呼び出し元が与えたデータタイプを使用して何を意図していたかを推測する必要がないためです。 isinstance(x、basestring)
の問題は、たとえば、型がベースストリングではない場合でも、それをストリングとして扱う必要があることを呼び出し側が通知する方法がないことです(別のシーケンスではありません。)おそらく、呼び出し側は、同じタイプを異なる目的に使用したい場合があります。時には単一のアイテムとして、また時にはアイテムのシーケンスとして。明示的であることは疑念をすべて取り除き、より堅牢で明確なコードにつながります。
他のヒント
すばらしい質問。私もこの問題に取り組んできましたが、「工場」に同意します; (クラスメソッドコンストラクター)は良いメソッドです。別のメソッドを提案したいと思います。これも非常に便利です。
サンプルです(これは read
メソッドであり、コンストラクタではありませんが、考え方は同じです):
def read(self, str=None, filename=None, addr=0):
""" Read binary data and return a store object. The data
store is also saved in the interal 'data' attribute.
The data can either be taken from a string (str
argument) or a file (provide a filename, which will
be read in binary mode). If both are provided, the str
will be used. If neither is provided, an ArgumentError
is raised.
"""
if str is None:
if filename is None:
raise ArgumentError('Please supply a string or a filename')
file = open(filename, 'rb')
str = file.read()
file.close()
...
... # rest of code
鍵となる考え方は、これを実装するために名前付き引数に対するPythonの優れたサポートを使用することです。ファイルからデータを読み取りたい場合、次のように言います:
obj.read(filename="blob.txt")
そして、文字列から読み取るには、次のように言います:
obj.read(str="\x34\x55")
この方法では、ユーザーが呼び出すメソッドは1つだけです。ご覧のように、内部での処理はあまり複雑ではありません
迅速かつダーティな修正
class MyData:
def __init__(string=None,list=None):
if string is not None:
#do stuff
elif list is not None:
#do other stuff
else:
#make data empty
その後、次のようにして呼び出すことができます
MyData(astring)
MyData(None, alist)
MyData()
より良い方法は、isinstanceと型変換を使用することです。私があなたを正しく理解しているなら、あなたはこれが欲しい:
def __init__ (self, filename):
if isinstance (filename, basestring):
# filename is a string
else:
# try to convert to a list
self.path = list (filename)
python3では、複数の実装を使用できます。 Pythonクックブックが書いたように、関数注釈付きのディスパッチ:
import time
class Date(metaclass=MultipleMeta):
def __init__(self, year:int, month:int, day:int):
self.year = year
self.month = month
self.day = day
def __init__(self):
t = time.localtime()
self.__init__(t.tm_year, t.tm_mon, t.tm_mday)
そして次のように動作します:
>>> d = Date(2012, 12, 21)
>>> d.year
2012
>>> e = Date()
>>> e.year
2018
isinstanceを使用する必要があります
isinstance(...)
isinstance(object, class-or-type-or-tuple) -> bool
Return whether an object is an instance of a class or of a subclass thereof.
With a type as second argument, return whether that is the object's type.
The form using a tuple, isinstance(x, (A, B, ...)), is a shortcut for
isinstance(x, A) or isinstance(x, B) or ... (etc.).
おそらく isinstance
組み込み関数が必要です:
self.data = data if isinstance(data, list) else self.parse(data)
私が好む解決策は:
class MyClass:
_data = []
__init__(self,data=None):
# do init stuff
if not data: return
self._data = list(data) # list() copies the list, instead of pointing to it.
次に、 MyClass()
または MyClass([1,2,3])
で呼び出します。
役立つことを願っています。ハッピーコーディング!
OK、素晴らしい。ファイル名ではなくタプルを使用してこの例をまとめましたが、それは簡単です。どうもありがとう。
class MyData:
def __init__(self, data):
self.myList = []
if isinstance(data, tuple):
for i in data:
self.myList.append(i)
else:
self.myList = data
def GetData(self):
print self.myList
a = [1,2]
b =(2,3)
c = MyData(a)
d = MyData(b)
c.GetData()
d.GetData()
[1、2]
[2、3]
なぜもっとPythonicにしないのですか?
class AutoList:
def __init__(self, inp):
try: ## Assume an opened-file...
self.data = inp.read()
except AttributeError:
try: ## Assume an existent filename...
with open(inp, 'r') as fd:
self.data = fd.read()
except:
self.data = inp ## Who cares what that might be?