การใช้ property () บน classmethods


173

ฉันมีคลาสที่มีวิธีการเรียนสองวิธี (โดยใช้ฟังก์ชั่น classmethod ()) สำหรับการรับและการตั้งค่าสิ่งที่เป็นตัวแปรคงที่เป็นหลัก ฉันพยายามใช้คุณสมบัติ () กับสิ่งเหล่านี้ แต่มันส่งผลให้เกิดข้อผิดพลาด ฉันสามารถทำซ้ำข้อผิดพลาดด้วยสิ่งต่อไปนี้ในล่าม:

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

เป็นไปได้ไหมที่จะใช้คุณสมบัติ () กับฟังก์ชั่นการตกแต่งแบบชั้นเรียน?

คำตอบ:


90

คุณสมบัติถูกสร้างบนคลาส แต่มีผลกับอินสแตนซ์ ดังนั้นหากคุณต้องการคุณสมบัติ classmethod ให้สร้างคุณสมบัติบน metaclass

>>> class foo(object):
...     _var = 5
...     class __metaclass__(type):  # Python 2 syntax for metaclasses
...         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

แต่เนื่องจากคุณใช้เมตาคลาสอยู่แล้วมันจะอ่านได้ดีขึ้นถ้าคุณย้ายเมธอดคลาสที่นั่น

>>> class foo(object):
...     _var = 5
...     class __metaclass__(type):  # Python 2 syntax for metaclasses
...         @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

หรือโดยใช้metaclass=...ไวยากรณ์ของ Python 3 และ metaclass ที่กำหนดไว้ภายนอกfooคลาสร่างกายและ metaclass ที่รับผิดชอบในการตั้งค่าเริ่มต้นเป็น_var:

>>> class foo_meta(type):
...     def __init__(cls, *args, **kwargs):
...         cls._var = 5
...     @property
...     def var(cls):
...         return cls._var
...     @var.setter
...     def var(cls, value):
...         cls._var = value
...
>>> class foo(metaclass=foo_meta):
...     pass
...
>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3

1
ดูเหมือนจะไม่เหมาะกับฉันใน Python 3.2 ถ้าฉันเปลี่ยน foo .__ metaclass __. var = property (foo.getvar.im_func, foo.setvar.im_func) เป็น foo. _ metaclass __. var = คุณสมบัติ (foo.getvar .__ func__, foo.setvar .__ func__) วัตถุ 'foo' ไม่มีแอตทริบิวต์ 'var' "เมื่อเรียกใช้งาน" foo.var "
Michael Kelley

การแก้ไขSIGHสองครั้ง: ใช้งานได้ใน Python 2.7 แต่ไม่ใช่ Python 3.2
Michael Kelley

@MichaelKelley - นั่นเป็นเพราะไวยากรณ์สำหรับmetaclasses มีการเปลี่ยนแปลงใน Python 3.x
mac

1
ฉันไม่แน่ใจที่จะเข้าใจสิ่งที่จะเป็นวิธี Python 3.x ที่จะเขียนสิ่งนี้แล้ว?
SylvainD

8
@Josay: คุณต้องกำหนดเมตาคลาสก่อนจากนั้นกำหนดคลาสโดยใช้class Foo(metaclass=...)ไวยากรณ์ใหม่
Kevin

69

การอ่านบันทึกประจำรุ่น Python 2.2ฉันพบสิ่งต่อไปนี้

เมธอด get [ของคุณสมบัติ] จะไม่ถูกเรียกเมื่อเข้าถึงคุณสมบัติเป็น class attribute (Cx) แทนที่จะเป็น attribute attribute (C (). x) หากคุณต้องการแทนที่การดำเนินการ __get__ สำหรับคุณสมบัติเมื่อใช้เป็นแอตทริบิวต์ class คุณสามารถ subclass property - เป็นประเภทรูปแบบใหม่เอง - เพื่อขยายเมธอด __get__ หรือคุณสามารถกำหนดชนิดตัวอธิบายได้ตั้งแต่เริ่มต้นด้วยการสร้างใหม่ คลาสสไตล์ที่กำหนดเมธอด __get__, __set__ และ __delete__

หมายเหตุ: วิธีการด้านล่างใช้ไม่ได้กับ setters แต่เป็น getters เท่านั้น

ดังนั้นฉันเชื่อว่าโซลูชันที่กำหนดคือการสร้าง 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

20
ฉันไม่คิดว่าส่วน setter ของ ClassProperty ใช้งานได้จริงตามที่อธิบายไว้: ในขณะที่ตัวอย่างการยืนยันทั้งหมดผ่านไปแล้วในตอนท้าย foo._var == 4 (ไม่ใช่ 3 ตามนัย) การตั้งค่าคุณสมบัติการปิดบังคุณสมบัติของตัวเอง เมื่อคุณสมบัติของคลาสถูกกล่าวถึงในpython-dev มันก็ชี้ให้เห็นว่าในขณะที่ getters เป็นเรื่องไม่สำคัญ setters เป็นเรื่องยาก (เป็นไปไม่ได้?) โดยไม่ต้อง metaclass
Gabriel Grant

4
@Gabriel ถูกต้องทั้งหมด ฉันไม่อยากจะเชื่อเลยว่าจะไม่มีใครชี้ให้เห็นว่าเป็นเวลาสองปี
agf

ฉันยังไม่แน่ใจว่าทำไมคุณไม่เพียงแค่ใช้self.fget(owner)และลบความจำเป็นที่จะต้องใช้@classmethodทั้งหมดที่นี่? (นั่นคือสิ่งที่classmethod จะแปล.__get__(instance, owner)(*args, **kwargs)การfunction(owner, *args, **kwargs)โทรผ่านตัวกลางสมบัติไม่จำเป็นต้องมีคนกลาง)
Martijn Pieters

การสาธิตของคุณขาดการเปลี่ยนแปลงที่เกิดขึ้นจริงไม่ว่าจะเป็นผู้ทะเยอทะยานหรือผู้ตั้งค่าที่จะแสดงให้เห็นอย่างเป็นระเบียบว่าการfoo.var = 3มอบหมายของคุณไม่ผ่านคุณสมบัติจริง ๆและแทนที่เพียงแค่แทนที่ออบเจ็กต์คุณสมบัติfooด้วยจำนวนเต็ม หากคุณเพิ่มการassert isinstance(foo.__dict__['var'], ClassProperty)โทรระหว่างการยืนยันของคุณคุณจะเห็นว่าfoo.var = 3การดำเนินการล้มเหลวหลังจากนั้น
Martijn Pieters

1
เรียนหลามทำคำอธิบายถึงไม่ได้รับการสนับสนุนที่มีผลผูกพันเกี่ยวกับการตั้งอยู่บนชั้นเองเท่านั้นที่ได้รับ (ดังนั้นinstance.attr, instance.attr = valueและdel instance.attrทั้งหมดผูกอธิบายจะพบได้type(instance)แต่ในขณะที่classobj.attrผูกclassobj.attr = valueและdel classobj.attrทำไม่ได้และแทนที่จะเปลี่ยนหรือลบวัตถุบ่งเอง) คุณต้องการ metaclass เพื่อรองรับการตั้งค่าและการลบ (ทำให้คลาสอ็อบเจ็กต์เป็นอินสแตนซ์และ metaclass เป็นประเภท)
Martijn Pieters

56

ฉันหวังว่า@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

2
สิ่งนี้ใช้ได้กับคลาสย่อยหรือไม่ (คลาสย่อยสามารถแทนที่
classproperties ได้

1
อืมใช่มั้ย class D(C): x = 2; assert D.x == 2
dtheodor

ฉันหวังว่ามันจะทำงานได้ดีเมื่อฉันใช้มันใน.formatลักษณะ"{x}".format(**dict(self.__class__.__dict__, **self.__dict__)):(
2rs2ts

@Nathan ไม่เพียง แต่ ... เมื่อคุณตั้งค่าคุณแทนที่ทุกการเข้าถึงโดยx 10ฉันชอบวิธีการนี้เพราะเป็นระเบียบและเรียบง่าย แต่ฟังดูเหมือน antipattern
Michele d'Amico

แก้ไขได้อย่างง่ายดาย: เพิ่ม a __set__ที่เพิ่มValueErrorเพื่อป้องกันการแทนที่
Kiran Jonnalagadda

30

เป็นไปได้ไหมที่จะใช้คุณสมบัติ () กับฟังก์ชั่นการตกแต่งแบบชั้นเรียน?

เลขที่

อย่างไรก็ตามคลาสของเมธอดเป็นเพียงเมธอดที่ถูกผูกไว้ (ฟังก์ชันบางส่วน) ในคลาสที่สามารถเข้าถึงได้จากอินสแตนซ์ของคลาสนั้น

เนื่องจากอินสแตนซ์เป็นฟังก์ชันของคลาสและคุณสามารถสืบทอดคลาสจากอินสแตนซ์ได้คุณสามารถรับพฤติกรรมตามที่คุณต้องการจากคลาสคุณสมบัติด้วย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')

และโปรดทราบว่าเราไม่ต้องการ metaclasses เลยและคุณไม่สามารถเข้าถึง metaclass โดยตรงผ่านอินสแตนซ์ของคลาส

เขียน@classpropertyมัณฑนากร

คุณสามารถสร้างclasspropertyมัณฑนากรได้ในโค้ดเพียงไม่กี่บรรทัดโดย subclassing property(มันถูกใช้ใน C แต่คุณสามารถเห็น Python ที่เทียบเท่าได้ที่นี่ ):

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()

แต่ฉันไม่แน่ใจว่าจะเป็นเช่นไร บทความรายชื่อผู้รับจดหมายเก่าแนะนำว่าไม่ควรใช้งาน

การรับคุณสมบัติเพื่อทำงานกับคลาส:

ข้อเสียของการดังกล่าวข้างต้นก็คือว่า "ทรัพย์สินคลาส" __dict__ไม่สามารถเข้าถึงได้จากชั้นเรียนเพราะมันก็จะเขียนทับให้คำอธิบายถึงข้อมูลจากชั้นเรียน

อย่างไรก็ตามเราสามารถแทนที่นี้กับสถานที่ให้บริการที่กำหนดไว้ใน __dict__metaclass ตัวอย่างเช่น:

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

จากนั้นคลาสอินสแตนซ์ของ 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'>

มีการเข้าถึงคุณสมบัติระดับ


3
ชัดเจนรัดกุมและละเอียด: นี่ควรเป็นคำตอบที่ยอมรับได้
pfabri

25

Python 3!

คำถามเก่า ๆ มีจำนวนการดูมากต้องการ Python 3 แบบทางเดียว

โชคดีที่มันง่ายสำหรับmetaclasskwarg:

class FooProperties(type):

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

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

จากนั้น >>> Foo.var

'FOO!


1
กล่าวคือไม่มีวิธีง่าย ๆ ออกจากกล่อง
เมห์เม็ต

@mehmet นี่มันไม่ง่ายเหรอ? Fooเป็นตัวอย่างของ metaclass ของตนและสามารถนำมาใช้วิธีการเดียวกับที่มันสามารถทำได้สำหรับผู้ที่กรณีของ@property Foo
OJFord

2
คุณต้องกำหนดคลาสอื่นสำหรับคลาสที่มีความซับซ้อนเป็นสองเท่าโดยสมมติว่าเมตาคลาสไม่สามารถใช้ซ้ำได้
เมห์เม็ต

เมธอด class ใช้งานได้จากทั้งคลาสและอินสแตนซ์ คุณสมบัตินี้ใช้งานได้จากคลาสเท่านั้น ฉันไม่คิดว่านี่คือสิ่งที่ถูกขอ
Aaron Hall

1
@AaronHall Foo.__new__หากที่สำคัญก็เพิ่มได้อย่างง่ายดายใน ถึงแม้ว่า ณ ตอนนั้นมันอาจจะคุ้มค่าที่จะใช้ getattribute แทนหรือตั้งคำถามว่าการแกล้งทำเป็นภาษาที่มีอยู่นั้นเป็นวิธีการที่คุณต้องการจริงๆ
OJFord

16

ไม่มีวิธีที่สมเหตุสมผลในการทำให้ระบบ "คุณสมบัติคลาส" นี้ทำงานใน Python

นี่คือวิธีหนึ่งที่ไม่สมเหตุสมผลในการทำให้มันใช้งานได้ คุณสามารถทำให้มันราบรื่นขึ้นด้วยการเพิ่มเมตาคลาสของเวทย์

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

เงื่อนของปัญหาคือคุณสมบัติเป็นสิ่งที่ Python เรียกว่า "descriptors" ไม่มีทางที่สั้นและง่ายต่อการอธิบายวิธีการเรียงลำดับของงาน metaprogramming นี้อยู่ดังนั้นฉันต้องชี้ให้คุณไปHOWTO บ่ง

คุณจำเป็นต้องเข้าใจสิ่งเหล่านี้หากคุณใช้กรอบงานที่ค่อนข้างสูง เช่นเดียวกับการคงอยู่ของวัตถุโปร่งใสหรือระบบ RPC หรือภาษาเฉพาะโดเมน

อย่างไรก็ตามในความคิดเห็นต่อคำตอบก่อนหน้าคุณพูดว่าคุณ

จำเป็นต้องปรับเปลี่ยนแอตทริบิวต์ที่ในลักษณะที่มองเห็นได้โดยอินสแตนซ์ทั้งหมดของคลาสและในขอบเขตที่วิธีการเรียนเหล่านี้ถูกเรียกว่าไม่มีการอ้างอิงถึงอินสแตนซ์ทั้งหมดของคลาส

สำหรับฉันสิ่งที่คุณต้องการจริงๆคือรูปแบบการออกแบบของผู้สังเกตการณ์


ฉันชอบไอเดียของตัวอย่างโค้ด แต่ดูเหมือนว่ามันจะเป็น clunky เล็กน้อยในทางปฏิบัติ
Mark Roddy

สิ่งที่ฉันพยายามทำให้สำเร็จคือการตั้งค่าตรงไปตรงมาและได้รับคุณลักษณะเดียวที่ใช้เป็นธงในการปรับเปลี่ยนพฤติกรรมของอินสแตนซ์ทั้งหมดดังนั้นฉันจึงคิดว่าผู้สังเกตการณ์จะแสดงความไม่พอใจต่อสิ่งที่ฉันพยายามทำ หากมีข้อสงสัยหลายประการฉันก็จะมีแนวโน้มมากกว่านี้
Mark Roddy

ดูเหมือนว่าการทำให้ฟังก์ชั่นเป็นที่เปิดเผยต่อสาธารณชนและเรียกพวกเขาโดยตรงว่าเป็นวิธีที่ง่ายกว่า ฉันอยากรู้ว่าถ้าฉันทำอะไรผิดหรือถ้าฉันพยายามทำมันเป็นไปไม่ได้ ขออภัยเกี่ยวกับความคิดเห็นหลายรายการโดยวิธี จำกัด จำนวนอักขระ 300 ตัว
Mark Roddy

สิ่งที่ดีเกี่ยวกับตัวอย่างรหัสคือคุณสามารถใช้บิต clunky ทั้งหมดครั้งเดียวแล้วสืบทอด เพียงย้าย _var ไปยังคลาสที่ได้รับมา class D1 (Foo): _var = 21 class D2 (Foo) _var = "Hello" D1.var 21 D2.var สวัสดี
Thomas L Holaday

6

การตั้งค่าเฉพาะในคลาสเมตาจะไม่ช่วยถ้าคุณต้องการเข้าถึงคุณสมบัติคลาสผ่านวัตถุที่สร้างอินสแตนซ์ในกรณีนี้คุณต้องติดตั้งคุณสมบัติปกติบนวัตถุด้วย (ซึ่งส่งไปยังคุณสมบัติคลาส) ฉันคิดว่าสิ่งต่อไปนี้ชัดเจนกว่านี้เล็กน้อย:

#!/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

3

ครึ่งโซลูชั่น __set__ ในชั้นเรียนยังใช้งานไม่ได้ การแก้ปัญหาคือคลาสคุณสมบัติที่กำหนดเองที่ใช้ทั้งคุณสมบัติและสแตติกวิธีการ

class ClassProperty(object):
    def __init__(self, fget, fset):
        self.fget = fget
        self.fset = fset

    def __get__(self, instance, owner):
        return self.fget()

    def __set__(self, instance, value):
        self.fset(value)

class Foo(object):
    _bar = 1
    def get_bar():
        print 'getting'
        return Foo._bar

    def set_bar(value):
        print 'setting'
        Foo._bar = value

    bar = ClassProperty(get_bar, set_bar)

f = Foo()
#__get__ works
f.bar
Foo.bar

f.bar = 2
Foo.bar = 3 #__set__ does not

3

เนื่องจากฉันต้องการแก้ไขแอตทริบิวต์ที่ในลักษณะที่มองเห็นได้โดยอินสแตนซ์ทั้งหมดของคลาสและในขอบเขตที่เรียกเมธอดคลาสเหล่านี้ไม่มีการอ้างอิงไปยังอินสแตนซ์ทั้งหมดของคลาส

คุณมีสิทธิ์เข้าถึงอินสแตนซ์ของคลาสอย่างน้อยหนึ่งรายการหรือไม่ ฉันสามารถคิดวิธีที่จะทำแล้ว:

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

2

ลองดูสิมันจะทำงานให้เสร็จโดยไม่ต้องเปลี่ยน / เพิ่มโค้ดที่มีอยู่มากมาย

>>> 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มีปากเสียง ให้พวกเขาห่อแลมบ์ดา (ซึ่งมันผ่านตัวอย่างเป็นอาร์กิวเมนต์แรก) และทั้งหมดเป็นอย่างดี


เมื่อ Florian Böschชี้ให้เห็นไวยากรณ์ที่จำเป็น (โดยห้องสมุดบุคคลที่สามหรือรหัสดั้งเดิม) คือ foo.var
โทมัส L Holaday

2

นี่คือวิธีแก้ปัญหาที่ควรใช้สำหรับการเข้าถึงผ่านคลาสและการเข้าถึงผ่านอินสแตนซ์ที่ใช้ 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

สิ่งนี้ยังทำงานกับ setter ที่กำหนดใน metaclass


1

หลังจากค้นหาสถานที่ต่าง ๆ ฉันพบวิธีกำหนด 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)

หวังว่านี่จะช่วยใครซักคน :)


1
หากวิธีการนี้เป็นสิ่งที่คุณพบที่ใดที่หนึ่งก็จะดีที่จะให้การเชื่อมโยง (ดูวิธีการวัสดุอ้างอิงที่เขียนโดยคนอื่น )
แอนดรูไมเออร์ส

-27

นี่คือคำแนะนำของฉัน อย่าใช้วิธีการเรียน

อย่างจริงจัง.

สาเหตุของการใช้วิธีการเรียนในกรณีนี้คืออะไร? ทำไมไม่มีวัตถุธรรมดาของชั้นธรรมดา?


หากคุณต้องการเปลี่ยนค่าอสังหาริมทรัพย์จะไม่เป็นประโยชน์จริง ๆ ไหม? เพียงแค่ตั้งค่าคุณสมบัติและทำได้ด้วย

ควรใช้สถานที่ให้บริการหากมีสิ่งที่ต้องปกปิด - สิ่งที่อาจเปลี่ยนแปลงในการนำไปใช้ในอนาคต

บางทีตัวอย่างของคุณอาจถูกปล้นและมีการคำนวณที่น่ากลัวที่คุณได้ละทิ้งไป แต่ดูเหมือนว่าคุณสมบัติจะเพิ่มมูลค่าที่สำคัญ

เทคนิค "ความเป็นส่วนตัว" ที่ได้รับอิทธิพลจาก Java (ใน Python ชื่อแอตทริบิวต์ที่ขึ้นต้นด้วย _) นั้นไม่มีประโยชน์จริง ๆ ส่วนตัวจากใคร จุดส่วนตัวเป็นสิ่งที่คลุมเครือเล็กน้อยเมื่อคุณมีแหล่งที่มา (เช่นเดียวกับใน Python)

ตัวรับและตัวตั้งค่า EJB ที่ได้รับอิทธิพลจาก Java (มักทำตามคุณสมบัติใน Python) มีไว้เพื่ออำนวยความสะดวกในการวิปัสสนาแบบจาวาดั้งเดิมของ Java ตัวรับและตัวตั้งค่าเหล่านั้นทั้งหมดไม่มีประโยชน์ใน Python


14
เนื่องจากฉันต้องการแก้ไขแอตทริบิวต์ที่ในลักษณะที่มองเห็นได้โดยอินสแตนซ์ทั้งหมดของคลาสและในขอบเขตที่เรียกเมธอดคลาสเหล่านี้ไม่มีการอ้างอิงถึงอินสแตนซ์ทั้งหมดของคลาส
Mark Roddy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.