PyObjectに関する質問への回答。 パート2

みなさんこんにちは。

これは、 pyobject.ruサイトからのPythonに関する質問とタスクへの回答の続きです。





免責事項

提案された回答は、最も正しいと見なされるべきではありません。 さらに、経験豊富な人がエラー、不正確さ、悪い場所を指摘してくれることを願っています。 事前に感謝します。




クラス



1.継承者を許可する基本クラスObservableを記述します。

a。 ** kwargsを送信する場合、対応する値を属性として入力します

b。 印刷ですべてのパブリック属性を表示する



属性にアクセスすると、次のことが起こります(ここで彼は嘘をつくことができます):

1.オブジェクト自体の属性の有無がチェックされます。 カスタム属性は、オブジェクトの__dict__属性に保存されます。

2.オブジェクトの__class __.__ dict__を介して、オブジェクトタイプの属性__dict__を確認しました

3.親はチェックされたタイプ

4. __getattribute__メソッドが新しいクラスに対して実行されます

5. __getattr__メソッドが実行されます



値を属性として使用できるようにするには、オブジェクトの__dict__属性を更新するだけで十分です。これは__init__メソッドで発生します。

__str__メソッドは、オブジェクトの「情報提供」を表示するために使用されます。 この場合、オブジェクトのすべてのパブリック属性とその値を表示します。



class Observable(object): """ Base class for attributes from dict. >>> class X(Observable): ... pass >>> x = X(foo=1, bar="Test", _barr='hidden', baz=(5, 6)) >>> print x X(bar=Test, foo=1, baz=(5, 6)) >>> print x.foo 1 >>> print x.bar Test >>> print x._barr hidden >>> print x.baz (5, 6) """ def __init__(self, **kwargs): self.__dict__.update(kwargs) def __str__(self): return '%s(%s)' % (self.__class__.__name__,\ (', '.join('%s=%s' % (key, val) for (key, val)\ in self.__dict__.iteritems() if not key.startswith('_'))))
      
      







2.すべての外部標識によって辞書になるが、属性としてキーへのアクセスを許可するクラスを作成します。

クラスがすべての外部記号によって辞書になるために、この辞書から継承します。 そして、キーが属性としてアクセスできるように、クラス内で__getattr__メソッドを定義します。このメソッドは、オブジェクト、そのタイプ、および親のディクショナリに属性が見つからないときに呼び出されます。



  class DictAttr(dict): """ Base class for JS-style dict. >>> x = DictAttr([('one', 1), ('two', 2), ('three', 3)]) >>> print x {'three': 3, 'two': 2, 'one': 1} >>> print x['three'] 3 >>> print x.get('two') 2 >>> print x.one 1 >>> print x.test Traceback (most recent call last): ... AttributeError """ def __getattr__(self, name): try: return self[name] except KeyError: raise AttributeError
      
      





3.ポイント2の複雑さ:継承者がget_KEYメソッドの存在によって動的に決定されるように、親クラスXDictAttrを記述します。

私はこの作業に多くの時間を費やしました。なぜなら、私はそれをなんとかファッショナブルで、スタイリッシュで、若々しく、松葉杖ではないからです。 何が起こったかはあなた次第です。



要するに、それがどのように機能するか:

1. __getitem__メソッドを使用すると、[]を介して呼び出しをインターセプトできます

メソッドに入ると、最初に、親__getitem__を呼び出して辞書にアクセスするだけで値を取得しようとします。 辞書で要素が見つからなかった場合は、__ getattr__メソッドを引数としてget_KEYを使用して値を取得しようとします

2. get()メソッドは、dict型の標準のget()動作をオーバーライドします。 まず、__ getattr__およびget_KEY引数を使用して属性を取得しようとします。 失敗した場合にのみ、親のgetメソッドを呼び出します。このメソッドは、未定義の__getitem__を呼び出し、辞書内の引数の存在を確認します。

3. __getattr__メソッドを使用すると、他のすべての状況を処理できます。 まず、親__getitem__を呼び出して値を取得しようとします。 そして、ここで、失敗の場合、汚いハックが出てきます。 __getattr__オブジェクトの呼び出しで発生するget_KEY属性を確認する必要があるため、再帰の可能性を除外する方法を理解できませんでした。 さて、あなたはポイントを得る。 最後に、get_get_get_get_get_get_get_fooのような行がありました。



この実装の欠点は、get_で始まる属性がAttributeErrorを発生させることです。 これはdoctestにマップされます。



  class XDictAttr(dict): """ >>> class X(XDictAttr): ... def get_foo(self): ... return 5 ... def get_bar(self): ... return 12 ... def get_get_z(self): ... return 42 >>> x = X({'one': 1, 'two': 2, 'three': 3}) >>> x {'one': 1, 'three': 3, 'two': 2} >>> x['one'] 1 >>> x.three 3 >>> x.bar 12 >>> x['foo'] 5 >>> x.get('foo', 'missing') 5 >>> x.get('bzz', 'missing') 'missing' >>> x.get_bar() 12 >>> x.get_foz() Traceback (most recent call last): ... AttributeError >>> x.get_get_z() 42 >>> x.get('get_z') 42 >>> x.get_z Traceback (most recent call last): ... AttributeError """ def __getattr__(self, name): try: return super(XDictAttr, self).__getitem__(name) except KeyError: if not name.startswith('get_'): return getattr(self, 'get_%s' % name)() else: raise AttributeError def __getitem__(self, key): try: return super(XDictAttr, self).__getitem__(key) except KeyError: return getattr(self, 'get_%s' % key)() def get(self, key, default=None): try: return getattr(self, 'get_%s' % key)() except AttributeError: return super(XDictAttr, self).get(key, default)
      
      





4.インスタンスを登録し、それらにイテレータインターフェイスを提供するクラスを作成する

私はPythonについてあまり知らないことを理解していますが、私にとっては、このタスクは次の理由でメタクラスに帰すべきです:

  >>> for i in Reg: ... print i <Reg instance at 0x98b6ecc> <Reg instance at 0x98b6fec> <Reg instance at 0x98ba02c>
      
      





この例は、クラスのオブジェクトではなく、型からイテレータを取得することを意味します。 pythonインタープリターは、__ iter__をclassmethodまたはstaticmethodでラップしようとしたときに、同様のエラーを宣誓しました。

したがって、私の実装は次のとおりです。

  class RegBase(type): def __iter__(cls): return iter(cls._instances) class Reg(object): """ >>> x = Reg() >>> x # doctest: +ELLIPSIS <__main__.Reg object at 0x...> >>> y = Reg() >>> y # doctest: +ELLIPSIS <__main__.Reg object at 0x...> >>> z = Reg() >>> z # doctest: +ELLIPSIS <__main__.Reg object at 0x...> >>> for i in Reg: # doctest: +ELLIPSIS ... print i <__main__.Reg object at 0x...> <__main__.Reg object at 0x...> <__main__.Reg object at 0x...> """ __metaclass__ = RegBase _instances = [] def __init__(self): self._instances.append(self)
      
      





PSはい。Regインスタンスの例では、Regオブジェクトがあります。 たぶん私の横棒はここにありますか?



メタクラスと記述子



おそらく最も議論の余地のあるセクションです。なぜなら、「メタクラスが必要かどうかわからないなら、それは必要ない」という理由で、私はメタクラスとは実質的に何の関係もないからです。 しかし、まだ試してみてください。



質問:

ドキュメント(データモデル)は引用しません。 さらに、私は次のように書きます。オブジェクト構造全般、特にメタクラスに関するHabréの良い記事があります。 ここここ 。 著者に感謝します。



タスク:

1.属性のタイプを修正する記述子を実装します。

言い換えると、記述子は、特定の動作を持つ新しい種類のクラスの属性です。

記述子に関する記事はこちらです。

  class Property(object): """ >>> class Image(object): ... height = Property(0) ... width = Property(0) ... path = Property('/tmp/') ... size = Property(0) >>> img = Image() >>> img.height = 340 >>> img.height 340 >>> img.path = '/tmp/x00.jpeg' >>> img.path '/tmp/x00.jpeg' >>> img.path = 320 Traceback (most recent call last): ... TypeError """ def __init__(self, value): self.__value = value self.__value_type = type(value) def __get__(self, obj, objtype=None): return self.__value def __set__(self, obj, value): if type(value) == self.__value_type: self.__value = value else: raise TypeError
      
      





2.属性タイプを修正する基本クラスを実装します(メタクラスを使用)。

ここで私は問題を抱えていました-私は仕事を完全に理解していませんでした。 著者の例に従って、属性height、pathはクラスです。 メタクラスがコミットしてオブジェクトに転送することを意図していたのか、それともコミットするだけなのかは明確ではありません。 2番目を実装しました。



Propertyクラスは、タスク1の回答と同じです。

必要なのは、作成するクラスのパブリック属性をプロパティでラップすることだけです。 メタクラスはクラス属性を持つ辞書を受け取るため、これは問題ではありません。

さらに、彼は属性がパブリックであるかメソッドであるかをチェックしました。

  class ImageMeta(type): def __new__(mcs, name, bases, dct): for key, val in dct.iteritems(): if not key.startswith('_') and not hasattr(val, '__call__'): dct[key] = Property(val) return type.__new__(mcs, name, bases, dct) class ImageBase(object): """ >>> class Image(ImageBase): ... height = 0 ... path = 'tmp' ... ... def foo(self): ... return 'bar' >>> img = Image() >>> img.height = 340 >>> img.height 340 >>> img.path = '/tmp/x00.jpeg' >>> img.path '/tmp/x00.jpeg' >>> img.path = 320 Traceback (most recent call last): ... TypeError >>> hasattr(img.foo '__call__') True """ __metaclass__ = ImageMeta
      
      





3.基本クラス(メタクラスを使用)と、クラスに基づいてモデルのSQLスキーマ(ANSI SQL)を作成する記述子を実装します。

私はこの方法をもっと美しくできるように思えるので、この方法についてコメントしてください。

実装のために行われたもの:

1.属性が作成された順に並べ替えることができるクラスカウンターを備えた基本的なプロパティ記述子。

2.独自の__str__を持つ整数およびStr記述子。モデルのSQL表現を作成するときに使用されます。

3.メタクラスTableMeta。モデルフィールドのリストを受け取り、モデルのSQL表現を作成します。

4.ベースクラステーブル。モデルのSQL表現を返すクラスメソッドを提供します。



  class Property(object): """ >>> class Image(object): ... size = Property(int) ... name = Property(basestring) >>> img = Image() >>> img.size = 0 >>> img.size 0 >>> img.name = '/tmp/img' >>> img.name '/tmp/img' >>> img.size = '~' Traceback (most recent call last): ... TypeError >>> img.name = ['/tmp/', 'img'] Traceback (most recent call last): ... TypeError >>> img.__class__.__dict__['size'].counter 0 >>> img.__class__.__dict__['name'].counter 1 """ counter = 0 def __init__(self, value_type): self.__value = None self.__value_type = value_type self.counter = Property.counter Property.counter += 1 def __get__(self, obj, objtype=None): return self.__value def __set__(self, obj, value): if isinstance(value, self.__value_type): self.__value = value else: raise TypeError class Integer(Property): def __init__(self): super(Integer, self).__init__(int) def __str__(self): return self.__class__.__name__.upper() class Str(Property): def __init__(self, size): super(Str, self).__init__(basestring) self.__size = size def __str__(self): return '{0}({1})'.format('varchar', self.__size).upper() class TableMeta(type): def __new__(mcs, name, bases, dct): fields = [(attr_name, val) for (attr_name, val) in dct.items()\ if isinstance(val, Property)] fields.sort(key=lambda x: x[1].counter) sql = ',\n'.join('\t{0} {1}'.format(attr_name, val)\ for (attr_name, val) in fields) dct['__sql'] = u'CREATE TABLE {0} (\n{1}\n)'.format(name, sql) return type.__new__(mcs, name, bases, dct) class Table(object): """ >>> class Image(Table): ... height = Integer() ... width = Integer() ... path = Str(128) >>> print Image.sql() # doctest: +NORMALIZE_WHITESPACE CREATE TABLE Image ( height INTEGER, width INTEGER, path VARCHAR(128) ) """ __metaclass__ = TableMeta @classmethod def sql(cls): return cls.__dict__['__sql']
      
      







以上です。 コメントと批判をありがとう。



All Articles