python مسلسل - باستخدام خاصية()على classmethods





قيامة ارطغرل (13)


هل من الممكن استخدام خاصية property () مع وظائف classmethod مزينة؟

لا.

ومع ذلك ، فإن classmethod هو ببساطة طريقة مرتبطة (وظيفة جزئية) على فئة يمكن الوصول إليها من مثيلات هذه الفئة.

نظرًا لأن المثيل هو وظيفة للفئة ويمكنك استنباط الفصل الدراسي من المثيل ، يمكنك الحصول على السلوك المطلوب الذي تريده من خاصية class-property مع property :

class Example(object):
    _class_property = None
    @property
    def class_property(self):
        return self._class_property
    @class_property.setter
    def class_property(self, value):
        type(self)._class_property = value
    @class_property.deleter
    def class_property(self):
        del type(self)._class_property

يمكن استخدام هذا الرمز للاختبار - يجب أن يمر دون إثارة أي أخطاء:

ex1 = Example()
ex2 = Example()
ex1.class_property = None
ex2.class_property = 'Example'
assert ex1.class_property is ex2.class_property
del ex2.class_property
assert not hasattr(ex1, 'class_property')

ولاحظ أننا لم نكن نحتاج إلى أي مقاييس - ولا يمكنك الوصول مباشرة إلى طبقة افتراضية عبر مثيلاتها الدراسية على أي حال.

كتابة @classproperty الديكور

يمكنك بالفعل إنشاء ديكور classproperty في بضعة أسطر من التعليمات البرمجية عن طريق subclassing property (يتم تنفيذه في C ، ولكن يمكنك مشاهدة Python المكافئ here ):

class classproperty(property):
    def __get__(self, obj, objtype=None):
        return super(classproperty, self).__get__(objtype)
    def __set__(self, obj, value):
        super(classproperty, self).__set__(type(obj), value)
    def __delete__(self, obj):
        super(classproperty, self).__delete__(type(obj))

ثم علاج الديكور كما لو كان classmethod جنبا إلى جنب مع الخاصية:

class Foo(object):
    _bar = 5
    @classproperty
    def bar(cls):
        """this is the bar attribute - each subclass of Foo gets its own.
        Lookups should follow the method resolution order.
        """
        return cls._bar
    @bar.setter
    def bar(cls, value):
        cls._bar = value
    @bar.deleter
    def bar(cls):
        del cls._bar

ويجب أن يعمل هذا الرمز بدون أخطاء:

def main():
    f = Foo()
    print(f.bar)
    f.bar = 4
    print(f.bar)
    del f.bar
    try:
        f.bar
    except AttributeError:
        pass
    else:
        raise RuntimeError('f.bar must have worked - inconceivable!')
    help(f)  # includes the Foo.bar help.
    f.bar = 5

    class Bar(Foo):
        "a subclass of Foo, nothing more"
    help(Bar) # includes the Foo.bar help!
    b = Bar()
    b.bar = 'baz'
    print(b.bar) # prints baz
    del b.bar
    print(b.bar) # prints 5 - looked up from Foo!


if __name__ == '__main__':
    main()

لكنني لست متأكداً من مدى حسن نية هذا. تقترح article قائمة بريدية قديمة أنه لا يجب أن تنجح.

الحصول على الخاصية للعمل في الفصل:

يتمثل الجانب السلبي أعلاه في أنه لا يمكن الوصول إلى "خاصية الفئة" من الفصل الدراسي ، نظرًا لأنها ستقوم بالكتابة فوق واصف البيانات من الفئة __dict__ .

ومع ذلك ، يمكننا تجاوز هذا مع خاصية محددة في metaclass __dict__ . فمثلا:

class MetaWithFooClassProperty(type):
    @property
    def foo(cls):
        """The foo property is a function of the class -
        in this case, the trivial case of the identity function.
        """
        return cls

ومن ثم يمكن أن يكون لطبقة class of the metaclass خاصية تقوم بالوصول إلى خاصية الفصل باستخدام المبدأ الموضح مسبقًا في الأقسام السابقة:

class FooClassProperty(metaclass=MetaWithFooClassProperty):
    @property
    def foo(self):
        """access the class's property"""
        return type(self).foo

والآن نرى كل من المثيل

>>> FooClassProperty().foo
<class '__main__.FooClassProperty'>

والطبقة

>>> FooClassProperty.foo
<class '__main__.FooClassProperty'>

لديك حق الوصول إلى خاصية الصف.

لدي فصل دراسي مع طريقتين للفصل الدراسي (باستخدام وظيفة classmethod () للحصول على وتحديد ما هو في الأساس متغير ثابت. حاولت استخدام الدالة property () مع هذه ، ولكن ينتج خطأ. تمكنت من إعادة إنتاج الخطأ بما يلي في المترجم:

class Foo(object):
    _var = 5
    @classmethod
    def getvar(cls):
        return cls._var
    @classmethod
    def setvar(cls, value):
        cls._var = value
    var = property(getvar, setvar)

يمكنني توضيح أساليب الصف ، لكنهم لا يعملون كخصائص:

>>> f = Foo()
>>> f.getvar()
5
>>> f.setvar(4)
>>> f.getvar()
4
>>> f.var
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: 'classmethod' object is not callable
>>> f.var=5
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: 'classmethod' object is not callable

هل من الممكن استخدام خاصية property () مع وظائف classmethod مزينة؟




لا توجد طريقة معقولة لجعل هذا النظام "خاصية الصف" يعمل في بايثون.

هنا طريقة واحدة غير معقولة لجعلها تعمل. يمكنك بالتأكيد جعلها أكثر سلاسة مع كميات متزايدة من سحر metaclass.

class ClassProperty(object):
    def __init__(self, getter, setter):
        self.getter = getter
        self.setter = setter
    def __get__(self, cls, owner):
        return getattr(cls, self.getter)()
    def __set__(self, cls, value):
        getattr(cls, self.setter)(value)

class MetaFoo(type):
    var = ClassProperty('getvar', 'setvar')

class Foo(object):
    __metaclass__ = MetaFoo
    _var = 5
    @classmethod
    def getvar(cls):
        print "Getting var =", cls._var
        return cls._var
    @classmethod
    def setvar(cls, value):
        print "Setting var =", value
        cls._var = value

x = Foo.var
print "Foo.var = ", x
Foo.var = 42
x = Foo.var
print "Foo.var = ", x

عقدة المشكلة هي أن الخصائص هي ما تسميه بايثون "الواصفات". لا توجد طريقة قصيرة وسهلة لتوضيح كيفية عمل هذا النوع من البرمجة ، لذا يجب أن أشير لك إلى الواصف howto .

تحتاج فقط إلى فهم هذا النوع من الأشياء إذا كنت تنفذ إطارًا متقدمًا إلى حد ما. مثل وجود كائن شفاف أو نظام RPC ، أو نوع من اللغة الخاصة بمجال معين.

ومع ذلك ، في تعليق على إجابة سابقة ، أنت تقول ذلك

تحتاج إلى تعديل سمة في مثل هذه الطريقة التي يتم رؤيتها من قبل كافة مثيلات فئة ما ، وفي النطاق الذي يتم استدعاء منه هذه الأساليب فئة لا يحتوي على مراجع إلى كافة مثيلات الفئة.

يبدو لي ، ما تريده حقاً هو نمط تصميم Observer .




وإليك الحل الذي يجب أن يعمل لكل من الوصول عبر الفصل والوصول عبر مثيل يستخدم metaclass.

In [1]: class ClassPropertyMeta(type):
   ...:     @property
   ...:     def prop(cls):
   ...:         return cls._prop
   ...:     def __new__(cls, name, parents, dct):
   ...:         # This makes overriding __getattr__ and __setattr__ in the class impossible, but should be fixable
   ...:         dct['__getattr__'] = classmethod(lambda cls, attr: getattr(cls, attr))
   ...:         dct['__setattr__'] = classmethod(lambda cls, attr, val: setattr(cls, attr, val))
   ...:         return super(ClassPropertyMeta, cls).__new__(cls, name, parents, dct)
   ...:

In [2]: class ClassProperty(object):
   ...:     __metaclass__ = ClassPropertyMeta
   ...:     _prop = 42
   ...:     def __getattr__(self, attr):
   ...:         raise Exception('Never gets called')
   ...:

In [3]: ClassProperty.prop
Out[3]: 42

In [4]: ClassProperty.prop = 1
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-4-e2e8b423818a> in <module>()
----> 1 ClassProperty.prop = 1

AttributeError: can't set attribute

In [5]: cp = ClassProperty()

In [6]: cp.prop
Out[6]: 42

In [7]: cp.prop = 1
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-7-e8284a3ee950> in <module>()
----> 1 cp.prop = 1

<ipython-input-1-16b7c320d521> in <lambda>(cls, attr, val)
      6         # This makes overriding __getattr__ and __setattr__ in the class impossible, but should be fixable
      7         dct['__getattr__'] = classmethod(lambda cls, attr: getattr(cls, attr))
----> 8         dct['__setattr__'] = classmethod(lambda cls, attr, val: setattr(cls, attr, val))
      9         return super(ClassPropertyMeta, cls).__new__(cls, name, parents, dct)

AttributeError: can't set attribute

هذا يعمل أيضا مع أداة تحديد في metaclass.




لا يساعد إعدادها على فئة التعريف فقط إذا كنت ترغب في الوصول إلى خاصية الفئة عبر كائن تم إنشاؤه ، في هذه الحالة تحتاج إلى تثبيت خاصية عادية على الكائن أيضًا (والتي ترسل إلى خاصية الفئة). أعتقد أن ما يلي أكثر وضوحًا قليلاً:

#!/usr/bin/python

class classproperty(property):
    def __get__(self, obj, type_):
        return self.fget.__get__(None, type_)()

    def __set__(self, obj, value):
        cls = type(obj)
        return self.fset.__get__(None, cls)(value)

class A (object):

    _foo = 1

    @classproperty
    @classmethod
    def foo(cls):
        return cls._foo

    @foo.setter
    @classmethod
    def foo(cls, value):
        cls.foo = value

a = A()

print a.foo

b = A()

print b.foo

b.foo = 5

print a.foo

A.foo = 10

print b.foo

print A.foo



بعد البحث في أماكن مختلفة ، وجدت طريقة لتعريف خاصية classproperty صالحة مع Python 2 و 3.

from future.utils import with_metaclass

class BuilderMetaClass(type):
    @property
    def load_namespaces(self):
        return (self.__sourcepath__)

class BuilderMixin(with_metaclass(BuilderMetaClass, object)):
    __sourcepath__ = 'sp'        

print(BuilderMixin.load_namespaces)

آمل أن هذا يمكن أن يساعد شخص ما :)




هنا اقتراحي. لا تستخدم طرق الصف.

بشكل جاد.

ما هو سبب استخدام طرق الصف في هذه الحالة؟ لماذا لا يكون لديك موضوع عادي لطبقة عادية؟

إذا كنت ترغب ببساطة في تغيير القيمة ، فإن العقار ليس مفيدًا حقًا في الواقع؟ ما عليك سوى تعيين قيمة السمة والقيام بها.

يجب استخدام الخاصية فقط إذا كان هناك شيء لإخفائه - وهو أمر قد يتغير في التنفيذ في المستقبل.

ربما تم تجريد المثال الخاص بك ، وهناك بعض الحسابات الجهنمية التي تركتها. ولكن لا يبدو أن الخاصية تضيف قيمة كبيرة.

تقنيات "الخصوصية" المتأثرة بالجافا (في Python ، أسماء السمات التي تبدأ بـ _) ليست مفيدة جدًا حقًا. خاص من من؟ النقطة الخاصة هي غامضة قليلاً عندما يكون لديك المصدر (كما تفعل في بايثون.)

إن حاصدي و مستقرات (EJB) المصممة على أساس جافا (غالباً ما يتم القيام بها كخصائص في بايثون) موجودة لتسهيل تأمل جافا البدائي بالإضافة إلى اجتياز حشد مع مترجم لغة ثابت. كل أولئك الذين يحصلون على حوافز أو مستفيدين ليسوا مساعدين في بايثون.




يتم إنشاء خاصية على فئة ولكنها تؤثر على مثيل. لذلك إذا كنت تريد خاصية classmethod ، قم بإنشاء الخاصية على metaclass.

>>> class foo(object):
    _var = 5
    class __metaclass__(type):
        pass
    @classmethod
    def getvar(cls):
        return cls._var
    @classmethod
    def setvar(cls, value):
        cls._var = value


>>> foo.__metaclass__.var = property(foo.getvar.im_func, foo.setvar.im_func)
>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3

ولكن بما أنك تستخدم metaclass على أي حال ، فستقرأ بشكل أفضل إذا قمت فقط بنقل classmethods هناك.

>>> class foo(object):
    _var = 5
    class __metaclass__(type):
        @property
        def var(cls):
            return cls._var
        @var.setter
        def var(cls, value):
            cls._var = value


>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3



جرب هذه المحاولة ، فهي تنجز المهمة دون الحاجة إلى تغيير / إضافة الكثير من الشفرات الموجودة.

>>> class foo(object):
...     _var = 5
...     def getvar(cls):
...         return cls._var
...     getvar = classmethod(getvar)
...     def setvar(cls, value):
...         cls._var = value
...     setvar = classmethod(setvar)
...     var = property(lambda self: self.getvar(), lambda self, val: self.setvar(val))
...
>>> f = foo()
>>> f.var
5
>>> f.var = 3
>>> f.var
3

تحتاج property إلى callable . منحهم lambda wrappers (الذي يمر المثيل كوسيطة الأولى) وكل شيء على ما يرام.




قراءة مذكرات الإصدار Python 2.2 ، أجد ما يلي.

لن يتم استدعاء أسلوب get [property] عند الوصول إلى الخاصية كسمة فئة (Cx) بدلاً من سمة مثيل (C (). x). إذا كنت تريد تجاوز عملية __get__ للخصائص عند استخدامها كسمة class ، فيمكنك استخدام خاصية فئة فرعية - وهي نوع جديد بنمط - لتوسيع طريقة __get__ الخاصة به ، أو يمكنك تعريف نوع واصف من الصفر عن طريق إنشاء نوع جديد. فئة الأسلوب التي تحدد أساليب __get__ و __set__ و __delete__.

ملاحظة: الطريقة أدناه لا تعمل في الواقع بالنسبة للمستوطنين ، فقط الحصول على.

لذلك ، أعتقد أن الحل المحدد هو إنشاء ClassProperty كفئة فرعية من الممتلكات.

class ClassProperty(property):
    def __get__(self, cls, owner):
        return self.fget.__get__(None, owner)()

class foo(object):
    _var=5
    def getvar(cls):
        return cls._var
    getvar=classmethod(getvar)
    def setvar(cls,value):
        cls._var=value
    setvar=classmethod(setvar)
    var=ClassProperty(getvar,setvar)

assert foo.getvar() == 5
foo.setvar(4)
assert foo.getvar() == 4
assert foo.var == 4
foo.var = 3
assert foo.var == 3

ومع ذلك ، لا يعمل المستوطنون في الواقع:

foo.var = 4
assert foo.var == foo._var # raises AssertionError

foo._var لم يتغير ، لقد قمت ببساطة بالكتابة فوق الخاصية بقيمة جديدة.

يمكنك أيضًا استخدام ClassProperty كديكور:

class foo(object):
    _var = 5

    @ClassProperty
    @classmethod
    def var(cls):
        return cls._var

    @var.setter
    @classmethod
    def var(cls, value):
        cls._var = value

assert foo.var == 5



أتمنى أن يساعد مصمم ديكور @classproperty للقراءة فقط @classproperty هذا في البحث عن شخصيات صفية.

class classproperty(object):

    def __init__(self, fget):
        self.fget = fget

    def __get__(self, owner_self, owner_cls):
        return self.fget(owner_cls)

class C(object):

    @classproperty
    def x(cls):
        return 1

assert C.x == 1
assert C().x == 1



بايثون 3!

السؤال القديم ، الكثير من وجهات النظر ، في حاجة ماسة إلى طريقة بايثون 3 صحيح واحد.

لحسن الحظ ، فمن السهل مع metaclass metaclass:

class FooProperties(type):

    @property
    def var(cls):
        return cls._var

class Foo(object, metaclass=FooProperties):
    _var = 'FOO!'

ثم ، >>> Foo.var

"فو!




لأنني في حاجة إلى تعديل سمة في مثل هذه الطريقة التي يتم مشاهدتها من قبل كافة مثيلات الفئة ، وفي النطاق الذي يتم استدعاء من خلاله طرق الفئات هذه ، لا تحتوي على مراجع لكافة مثيلات الفئة.

هل لديك حق الوصول إلى مثيل واحد على الأقل من الفصل الدراسي؟ يمكنني التفكير في طريقة للقيام بذلك بعد ذلك:

class MyClass (object):
    __var = None

    def _set_var (self, value):
        type (self).__var = value

    def _get_var (self):
        return self.__var

    var = property (_get_var, _set_var)

a = MyClass ()
b = MyClass ()
a.var = "foo"
print b.var



حاولت كتابة مثال بسيط لإظهار الفرق بين استخدام staticmethod و classmethod (وكذلك عدم استخدامها). هنا الرمز:

#without decorator:
class Duck(object):
    def sound(self):
        print("Quack")


#pay attention to the "sound" function:

#with @staticmethod
class Cat(object):
    @staticmethod
    def sound():
        print("Meow")

#with @classmethod
class Dog(object):
    @classmethod
    def sound(self):
        print("woof")


#notice the differences between calling them:
Duck().sound()
Cat.sound()
Dog.sound()

#prints:
"""
Quack
Meow
woof
"""






python oop