2013-10-08 9 views
8

のサブクラスの属性を設定することはできませんそれは私がnamedtupleのサブクラスを作成し、異なる提供しようとしているthisまたはthisはやや関連スレッドですように見えますが、それでも物事を考え出したていない:)がnamedtuple

を私はさまざまな方法でオブジェクトを構築することができます。動作しません。しかし

>>> from collections import namedtuple 
>>> class C(namedtuple("C", "x, y")) : 
...  __slots__ =() 
...  def __init__(self, obj) : # Initialize a C instance by copying values from obj 
...   self.x = obj.a 
...   self.y = obj.b 
...  def __init__(self, x, y) : # Initialize a C instance from the parameters 
...   self.x = x 
...   self.y = y 

、:たとえば

>>> c = C(1, 2) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 7, in __init__ 
AttributeError: can't set attribute 

一部突っつい周りの後に(例えば、thisスレッドを参照してください)私の代わりに初期化子のコンストラクタを使用しようとしました:

>>> from collections import namedtuple 
>>> class C(namedtuple("C", "x, y")) : 
...  __slots__ =() 
...  def __new__(cls, obj) : 
...  self = super(C, cls).__new__(cls, obj.a, obj.b) 
...  def __new__(cls, x, y) : 
...  self = super(C, cls).__new__(cls, x, y) 

オブジェクトを作成したようですが、その属性を読み取ることができません:

>>> c = C(1,2) 
>>> c.x, c.y 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'NoneType' object has no attribute 'x' 

ここで私は間違っていますか?複数のコンストラクタまたはイニシャライザを使用してサブクラスを作成するにはどうすればよいですか?

+0

なぜ、 '__init__'と' __new__'メソッドが二重になっていますか? 2番目のものだけがカウントされ、最初のものを上書きします。 Pythonはメソッドシグネチャを「オーバーロード」しません。 –

+0

オーバーロードがありません...つまり、Cのインスタンスを作成するという私の本来の目的は、(オーバーロードされたコンストラクタによって)実際に実行できません。 – Jens

+0

これは完璧に実行可能で、異なるパラダイムを使用しています。 –

答えて

18

名前付きタプルは不変なので、__init__イニシャライザで操作することはできません。 __new__は、新しいインスタンスのためのファクトリメソッドですので、あなたがリターン新しく作成されたインスタンスに必要ないということ

class C(namedtuple('C', 'x, y')): 
    __slots__ =() 
    def __new__(cls, obj): 
     return super(C, cls).__new__(cls, obj.x, obj.y) 

注:あなたの唯一のオプションは、__new__メソッドをオーバーライドすることです。 __new__メソッドでreturnを使用しない場合、デフォルトの戻り値はNoneで、エラーが発生します。 xy属性を持つオブジェクトと

デモ:

>>> class C(namedtuple('C', 'x, y')): 
...  __slots__ =() 
...  def __new__(cls, obj): 
...   return super(C, cls).__new__(cls, obj.x, obj.y) 
... 
>>> O.x, O.y 
(10, 20) 
>>> C(O) 
C(x=10, y=20) 

Pythonはメソッドのオーバーロードをサポートしていません。一般的には、ファクトリメソッドとしてオプションのキーワード引数または余分なクラスメソッドを使用します。

例えば、datetime moduleには、標準コンストラクタに適合しないオブジェクトを作成するためのいくつかのファクトリメソッドがあります。 datetime.datetime.fromtimestamp()は、datetime.datetimeインスタンスを1つの数値から作成します(datetime.datetime.fromordinal()も同様です)。それらは異なる方法で数を解釈する点を除いて。

あなたは変数引数をサポートしたい場合は、操作を行います。

>>> C(3, 5): 
C(x=3, y=5) 
>>> C(O) 
C(x=10, y=20) 

代替使用して、:呼び出し側によって提供されていない場合はここで

class C(namedtuple('C', 'x, y')): 
    __slots__ =() 

    def __new__(cls, x, y=None): 
     if y is None: 
      # assume attributes 
      x, y = x.x, x.y 
     return super(C, cls).__new__(cls, x, y) 

yNoneをデフォルト、オプションの引数でありますクラスメソッドは次のようになります。

class C(namedtuple('C', 'x, y')): 
    @classmethod 
    def from_attributes(cls, obj): 
     return cls(obj.x, obj.y) 

2つのファクトリメソッド。 1つのデフォルトと名付け1:

>>> C(3, 5): 
C(x=3, y=5) 
>>> C.from_attributes(O) 
C(x=10, y=20) 
+0

ありがとうMartijn。オーバーロードがなければ、コンストラクタ( 'x'と' y'を受け取る)と第2のファクトリメソッド( 'obj'を受け取る)を使うことにしました。きれいではないかもしれませんが、おそらくC++スタイルのコンストラクタのオーバーロードが好きかもしれませんが、それは私がPythonでできることだと思います。 – Jens

1

2つのこと:1、あなたは本当に私の知る限り、ここでnamedtupleのうちの多くを取得していません。だから、普通のクラスに切り替えるだけでいいのかもしれない。また、あなたが

セカンドをオーバーロードすることはできません、あなたの問題に役立つかもしれない他の可能性:

Factory design pattern - 代わりに、コンストラクタで異なるパラメータを置くのは、パラメータや通話の種類を取るクラスを持っていますオブジェクトの外側に適切な引数を持つコンストラクタ recordtype - 変更可能な名前付きタプル。デフォルトを許可しますが、元の方法でサブクラスを書き込むこともできます。 bunch - 厳密に名前付きタプルではありませんが、幾分任意のオブジェクトを作成することができます。

+0

リンクをありがとう、これらはいい言及です! – Jens

0

名前付きタプルの属性を変更するには、回避策があります。

import collections 

def updateTuple(NamedTuple,nameOfNamedTuple): 
    ## Convert namedtuple to an ordered dictionary, which can be updated 
    NamedTuple_asdict = NamedTuple._asdict() 

    ## Make changes to the required named attributes 
    NamedTuple_asdict['path']= 'www.google.com' 

    ## reconstruct the namedtuple using the updated ordered dictionary 
    updated_NamedTuple = collections.namedtuple(nameOfNamedTuple, NamedTuple_asdict.keys())(**NamedTuple_asdict) 

    return updated_NamedTuple 

Tuple = collections.namedtuple("Tuple", "path") 
NamedTuple = Tuple(path='www.yahoo.com') 
NamedTuple = updateTuple(NamedTuple, "Tuple") 
+1

これは名前付きタプルの新しいインスタンスを作成します。名前付きタプルへの既存の参照はすべて元のものにアクセスし、新しいタプルにはまだアクセスします。したがって、彼らは変化を見ることはありません。 – Jens

+0

新しいインスタンスを古い変数に割り当てることができませんでしたか? – chahuja

+0

実行時に元のタプルを指す* all *変数を追跡している場合は、yesを返します。 – Jens

関連する問題