リストである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 Cookbookが書いたように、 Function Annotationsを使用した複数のディスパッチの実装 を使用できます。
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])
で呼び出します。
お役に立てば幸いです。ハッピーコーディング!