3

Pythonでは、オブジェクトを定義した後に新しいクラス変数が追加されないようにする方法はありますか?

例えば:

class foo:
    def __init__(self):
        self.a = 1
        self.b = 2
        self.c = 3

bar = foo()
try:
    bar.d = 4
except Exception, e:
    print "I want this to always print"

あるいは、オブジェクト内の変数の数を数える方法はありますか?

class foo:
    def __init__(self):
        self.a = 1
        self.b = 2
        self.c = 3
    def count(self):
        ...

bar = foo()
if bar.count() == 3:
    print "I want this to always print"

私がこれを行うことを考えた唯一の方法は、辞書またはリストを使用することでした。

class foo:
    def __int__(self):
        self.dict = {'foo':1, 'bar':2} 
        self.len  = 2
    def chk():
        return self.len == len(self.list)

ただし、これを行うことはPythonにとってかなり面倒です。(obj.dict ['foo'])。可能であれば、obj.fooだけを使用したいと思います。

既存の変数を変更するときに誤って変数を宣言しないように、これが必要です。

f = foo()
f.somename = 3
...
f.simename = 4 #this is a typo

if f.somename == 3:
    solve_everything()
4

7 に答える 7

5

__setattr__の奇妙なことを避けるために使用することをお勧めし__slots__ます。

で設定したものを含むすべてのインスタンス属性__setattr__の設定を処理するため、をいじるときは常に注意する必要があります。したがって、属性の設定をいつ許可するか、いつ拒否するかを知るための何らかの方法が必要です。このソリューションでは、新しい属性を許可するかどうかを制御する特別な属性を指定しました。__init__

class A(object):
    def __init__(self):
        self.a = 1
        self.b = 2
        self.c = 3
        self.freeze = True

    def __setattr__(self, attr, value):
        if getattr(self, "freeze", False) and not hasattr(self, attr):
            raise AttributeError("You shall not set attributes!")
        super(A, self).__setattr__(attr, value)

テスト:

a = A()
try:
    a.d = 89
except AttributeError:
    print "It works!"
else:
    print "It doesn't work."
a.c = 42
print a.a
print a.c
a.freeze = False
a.d = 28
a.freeze = True
print a.d

結果:

できます!
1
42
28

また、この概念をクラスデコレータにきちんとまとめているgnibblersの回答も参照してください。これにより、クラス定義が乱雑にならず、コードを複製せずに複数のクラスで再利用できます。


編集:

1年後にこの回答に戻ると、コンテキストマネージャーがこの問題をさらにうまく解決できる可能性があることに気付きました。gnibblerのクラスデコレータの修正バージョンは次のとおりです。

from contextlib import contextmanager

@contextmanager
def declare_attributes(self):
    self._allow_declarations = True
    try:
        yield
    finally:
        self._allow_declarations = False

def restrict_attributes(cls):
    cls.declare_attributes = declare_attributes
    def _setattr(self, attr, value):
        disallow_declarations = not getattr(self, "_allow_declarations", False)
        if disallow_declarations and attr != "_allow_declarations":
            if not hasattr(self, attr):
                raise AttributeError("You shall not set attributes!")
        super(cls, self).__setattr__(attr, value)
    cls.__setattr__ = _setattr

    return cls

そして、これがそれを使用する方法です:

@restrict_attributes
class A(object):
    def __init__(self):
        with self.declare_attributes():
            self.a = 1
            self.b = 2
            self.c = 3

したがって、新しい属性を設定する場合は、with上記のステートメントを使用してください。インスタンスの外部から実行することもできます。

a = A()
try:
    a.d = 89
except AttributeError:
    print "It works!"
else:
    print "It doesn't work."
a.c = 42
print a.a
print a.c
with a.declare_attributes():
    a.d = 28
print a.d
于 2012-08-15T06:41:23.027 に答える
4

Pythonでは、オブジェクトを定義した後に新しいクラス変数が追加されないようにする方法はありますか?

はい。__slots__。ただし、注意事項を注意深くお読みください

于 2012-08-15T06:24:15.433 に答える
3

lazyrの答えに基づいたクラスデコレータはどうですか

def freeze(cls):
    _init = cls.__init__
    def init(self, *args, **kw):
        _init(self, *args, **kw)
        self.freeze = True
    cls.__init__ = init 

    def _setattr(self, attr, value):
        if getattr(self, "freeze", None) and (attr=="freeze" or not hasattr(self, attr)):
            raise AttributeError("You shall not set attributes!")
        super(cls, self).__setattr__(attr, value)
    cls.__setattr__ = _setattr

    return cls

@freeze
class foo(object):
    def __init__(self):
        self.a = 1
        self.b = 2
        self.c = 3


bar = foo()
try:
    bar.d = 4
except Exception, e:
    print "I want this to always print"
于 2012-08-15T07:12:08.003 に答える
2
  1. __slots__クラス属性を使用して新しい属性を追加できないようにする:

    class foo(object):
        __slots__ = ['a', 'b', 'c']
        def __init__(self):
            self.a = 1
            self.b = 2
            self.c = 3
    
    bar = foo()
    
    try:
        bar.d = 4
    except Exception as e:
        print(e,"I want this to always print")
    
  2. 属性のカウント:

    print(len([attr for attr in dir(bar) if attr[0] != '_' ]))
    
于 2012-08-15T06:29:19.737 に答える
1

これを使用して、インスタンスの属性の数をカウントします。

>>> class foo:
    def __init__(self):
        self.a = 1
        self.b = 2
        self.c = 3


>>> bar=foo()
>>> bar.__dict__
{'a': 1, 'c': 3, 'b': 2}
>>> len(bar.__dict__)  #returns no. of attributes of bar
3
于 2012-08-15T06:25:01.040 に答える
1

新しいクラス変数または新しいインスタンス変数を意味しますか?後者はあなたが言っているように見え、はるかに簡単です。

Ignacio Vazquez-Abramsの答えによる__slots__と、おそらくあなたが望むものです。クラス内で行うだけ__slots__ = ('a', 'b', 'c')で、他の属性が作成されなくなります。これはクラスのインスタンスにのみ適用されることに注意してください。クラスレベルの属性は引き続き設定でき、サブクラスは任意の属性を追加できます。そして彼は正しいです-いくつかの奇妙なことがあるので、どこにでもスロットを振りかける前にリンクされたドキュメントを読んでください。

スロットを使用していない場合はreturn len(vars(self))、提案された方法の本体として機能しますcount

スロットの代わりに__setattr__、「既知の良好」リストにない属性を拒否する、またはfrozen属性が最後にTrueに設定された後に新しい属性を拒否するなどを定義できます__init__。これは正しく理解するのが困難です。しかし、より柔軟です。

初期化後にインスタンスを完全に読み取り専用にしたい場合で、最新バージョンのPythonを使用している場合は、namedtupleそのサブクラスまたはそのサブクラスを定義することを検討してください。ただし、タプルサブクラスにもいくつかの制限があります。あなたがこのルートに行く必要があるなら、私はそれを拡張することができます、しかしあなたがそうでない理由がない限り、私はスロットに固執します。

于 2012-08-15T06:39:19.390 に答える
0

クラスに可変属性と不変属性の両方の固定セットを持たせたいとしましょう。init後にクラス属性を不変にするためにgnibblerの答えをハックしました:

def frozenclass(cls):
    """ Modify a class to permit no new attributes after instantiation.
        Class attributes are immutable after init.
        The passed class must have a superclass (e.g., inherit from 'object').
    """
    _init = cls.__init__
    def init(self, *args, **kw):
        _init(self, *args, **kw)
        self.freeze = True
    cls.__init__ = init

    def _setattr(self, attr, value):
        if getattr(self, "freeze", None):
            if attr=="freeze" or not hasattr(self, attr):
                raise AttributeError("You shall not create attributes!")
            if hasattr(type(self), attr):
                raise AttributeError("You shall not modify immutable attributes!")
        super(cls, self).__setattr__(attr, value)
    cls.__setattr__ = _setattr

    return cls

そして例:

@frozenclass
class myClass(object):
    """ A demo class."""
    # The following are immutable after init:
    a = None
    b = None
    c = None

    def __init__(self, a, b, c, d=None, e=None, f=None):
        # Set the immutable attributes (just this once, only during init)
        self.a = a
        self.b = b
        self.c = c
        # Create and set the mutable attributes (modifyable after init)
        self.d = d
        self.e = e
        self.f = f
于 2012-09-26T21:14:32.080 に答える