リストであるデータと呼ばれるメンバーを持つクラスがあるとしましょう。
たとえば、ファイル名(リストを初期化するためのデータを含む)または実際のリストでクラスを初期化できるようにしたいと考えています。
これを行うためのあなたのテクニックは何ですか?
を見て型を確認するだけ__class__
ですか?
私が見逃しているかもしれないトリックはありますか?
私は、引数の型によるオーバーロードが簡単な C++ に慣れています。
リストであるデータと呼ばれるメンバーを持つクラスがあるとしましょう。
たとえば、ファイル名(リストを初期化するためのデータを含む)または実際のリストでクラスを初期化できるようにしたいと考えています。
これを行うためのあなたのテクニックは何ですか?
を見て型を確認するだけ__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)
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)
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]
私の好ましい解決策は次のとおりです。
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])
。
それが役立つことを願っています。ハッピーコーディング!
もっと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?