วิธีการเพิ่มคุณสมบัติให้กับคลาสแบบไดนามิก?


215

เป้าหมายคือการสร้างคลาสจำลองซึ่งทำหน้าที่เหมือนชุดผลลัพธ์ db

ตัวอย่างเช่นถ้าเคียวรีฐานข้อมูลส่งคืนโดยใช้นิพจน์ dict {'ab':100, 'cd':200}แล้วฉันต้องการดู:

>>> dummy.ab
100

ตอนแรกฉันคิดว่าฉันอาจทำแบบนี้ได้:

ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
    def __init__(self, ks, vs):
        for i, k in enumerate(ks):
            self[k] = vs[i]
            setattr(self, k, property(lambda x: vs[i], self.fn_readyonly))

    def fn_readonly(self, v)
        raise "It is ready only"

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

แต่c.abส่งคืนออบเจกต์คุณสมบัติแทน

การแทนที่setattrบรรทัดด้วยk = property(lambda x: vs[i])ไม่ใช้เลย

ดังนั้นวิธีที่ถูกต้องในการสร้างคุณสมบัติอินสแตนซ์ที่รันไทม์คืออะไร?

PS ฉันรู้ทางเลือกที่นำเสนอในวิธีการคือ__getattribute__วิธีการใช้งานอย่างไร


2
มีความผิดพลาดน้อยในรหัสของคุณ: ความหมายของ fn_readonly ความต้องการ:และการอ้างอิง__init__ self.fn_readyonly
mhawke

คุณพูดถูก ฉันเพิ่มฟังก์ชัน setter ในนาทีสุดท้ายเพื่อขีดเส้นใต้เหตุผลของการสร้างคุณสมบัติใน runtime
Anthony Kong

ปัญหาหลักที่ฉันมีกับการสร้างคุณสมบัติในการเริ่มต้นก็คือในบางกรณีถ้าฉันเรียกผู้ช่วยหลังจากนั้นอย่างรวดเร็วหรือมีปัญหาฉันจะได้รับข้อผิดพลาดที่พวกเขาไม่มีอยู่แม้ว่าพวกเขาจะทำ ในโซลูชันของฉันด้านล่างฉันสร้าง 2 คลาส หนึ่งในนั้นเป็น Base / Parent (ซึ่งฉันพยายามค้นหาวิธีแก้ปัญหา) และวัตถุหลักซึ่งขยาย Base / Parent จากนั้นในวัตถุหลักโดยไม่เริ่มต้นฉันเรียกผู้สร้าง AccessorFunc ของฉันซึ่งสร้างคุณสมบัติฟังก์ชั่นตัวช่วยและอีกมากมาย
Acecool

เช่น: คลาส ExampleBase: pass; ตัวอย่างคลาส (ExampleBase): __x = Accessor (ExampleBase, 'x', 'X', 123); --- ซึ่งจะสร้างคุณสมบัติภายใต้ x และฟังก์ชั่นที่มีชื่อโดยใช้ X so GetX, SetX และอื่น ๆ ... และ. x, ._x และ. _ x สำหรับคุณสมบัติ ดังนั้น. x จึงเป็นคุณสมบัติสำหรับข้อมูลที่จะผ่าน (การรับ / การตั้งค่าผ่าน self.x = 123 หรือ self.x เพื่อส่งออก) ฉันใช้ self._x สำหรับข้อมูล RAW ที่จัดเก็บไว้เพื่อให้สามารถเข้าถึงได้ง่ายเพราะฉันยังอนุญาตให้กำหนดค่าเริ่มต้นโดยไม่ต้องตั้งค่าเหล่านั้นในข้อมูลที่เก็บไว้ ดังนั้น _x อาจเป็น None และ. x สามารถส่งคืน 123 และ. ___ เชื่อมโยงกับ Accessor
Acecool

นี่คือลิงค์ไปยังเวอร์ชั่นพื้นฐานซึ่งสร้างคุณสมบัติแบบไดนามิกและฟังก์ชั่นแบบไดนามิก - ไฟล์มีลิงค์ไปยังเวอร์ชั่นอื่น ๆ หนึ่งคือระบบ AccessorFunc ที่ใช้ฟังก์ชั่นในการสร้างผู้ช่วยเหลือ (หนึ่งสำหรับฟังก์ชั่นหนึ่งสำหรับคุณสมบัติหนึ่งสำหรับทั้งเป็นองค์ประกอบแต่ละคน - ดังนั้นมันจะไม่ใช้การย่อรหัสในไฟล์ใด ๆ ในไฟล์นั้น .. ) หากมีสิ่งใดขาดหายไป ไฟล์อื่น ๆ มีอยู่: dropbox.com/s/phnnuavssmzeqrr/dynamic_properties_simple.py?dl=0
Acecool

คำตอบ:


333

ฉันคิดว่าฉันควรขยายคำตอบนี้ตอนนี้ฉันแก่แล้วและฉลาดขึ้นและรู้ว่าเกิดอะไรขึ้น มาสายดีกว่าไม่มาเลย.

คุณสามารถเพิ่มคุณสมบัติให้กับคลาสแบบไดนามิก แต่นั่นคือสิ่งที่จับได้: คุณต้องเพิ่มมันเข้าไปในชั้น

>>> class Foo(object):
...     pass
... 
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4

propertyเป็นจริงการดำเนินงานที่เรียบง่ายของสิ่งที่เรียกว่าให้คำอธิบาย มันเป็นวัตถุที่ให้กำหนดเองการจัดการสำหรับแอตทริบิวต์ที่กำหนดในระดับที่กำหนด ค่อนข้างเป็นวิธีแยกifต้นไม้ขนาดใหญ่ออกจาก__getattribute__จากต้นไม้แห่ง

เมื่อผมถามfoo.bในตัวอย่างข้างต้น, Python เห็นว่าbกำหนดไว้ในการดำเนินการชั้นโปรโตคอลบ่ง -which ก็หมายความว่ามันเป็นวัตถุที่มีหนึ่ง__get__, __set__หรือ__delete__วิธีการ descriptor อ้างความรับผิดชอบในการจัดการแอตทริบิวต์นั้นดังนั้นการเรียก Python Foo.b.__get__(foo, Foo)และค่าส่งคืนจะถูกส่งกลับมาให้คุณเป็นค่าของแอตทริบิวต์ ในกรณีของpropertyแต่ละวิธีการเหล่านี้เพียงแค่เรียกfget, fsetหรือfdelคุณส่งผ่านไปยังpropertyตัวสร้าง

Descriptors เป็นวิธีการของ Python ในการเปิดเผยการใช้งาน OO ทั้งหมด propertyในความเป็นจริงมีประเภทของการให้คำอธิบายอื่นมากยิ่งขึ้นกว่ากัน

>>> class Foo(object):
...     def bar(self):
...         pass
... 
>>> Foo().bar
<bound method Foo.bar of <__main__.Foo object at 0x7f2a439d5dd0>>
>>> Foo().bar.__get__
<method-wrapper '__get__' of instancemethod object at 0x7f2a43a8a5a0>

วิธีอ่อนน้อมถ่อมตนเป็นเพียงตัวบ่งบอกประเภทอื่น มัน__get__tacks บนอินสแตนซ์เรียกเป็นอาร์กิวเมนต์แรก; ผลก็คือ:

def __get__(self, instance, owner):
    return functools.partial(self.function, instance)

อย่างไรก็ตามฉันสงสัยว่านี่เป็นเหตุผลที่ descriptor ใช้งานได้เฉพาะในชั้นเรียนเท่านั้น: พวกมันเป็นระเบียบแบบแผนของสิ่งที่ให้พลังแก่ชั้นเรียนในตอนแรก พวกเขายังเป็นข้อยกเว้นสำหรับกฎ: คุณสามารถกำหนด descriptor ให้กับคลาสได้อย่างชัดเจนและคลาสเป็นตัวอย่างของtype! ในความเป็นจริงพยายามอ่านFoo.bยังคงโทรproperty.__get__ ; มันเป็นเพียงสำนวนที่บ่งบอกว่าจะกลับมาเองเมื่อเข้าถึงเป็นแอตทริบิวต์ของคลาส

ฉันคิดว่ามันค่อนข้างดีที่ระบบ OO ของ Python เกือบทั้งหมดสามารถแสดงใน Python ได้ :)

โอ้และฉันเขียนบล็อกโพสต์คำเกี่ยวกับ descriptorsในขณะที่ถ้าคุณสนใจ


35
ไม่จำเป็นต้องเพิ่มเมธอด add_property setattr (Foo, 'name', property (func))
Courtney D

8
ของคุณ "แต่นั่นคือสิ่งที่จับได้ ... " เพิ่งช่วยฉันทำงานหลายชั่วโมง ขอบคุณ.
Matt Howell

2
หากคุณต้องการที่จะกำหนดสถานที่ให้บริการบนเช่นเดียวคุณสามารถสร้างชั้นที่รันไทม์และปรับเปลี่ยน __class__
Wilfred Hughes

1
แล้ว @ myproperty.setter ล่ะ จะเพิ่มมันแบบไดนามิคได้อย่างไร?
LRMAAX

คุณไม่จำเป็นต้องเพิ่มคุณสมบัติให้กับวัตถุเริ่มต้น การทำเช่นนั้นอาจหมายถึงมันติดอยู่กับอินสแตนซ์เท่านั้น แต่ฉันต้องตรวจสอบอีกครั้งว่า ฉันรู้ว่าฉันพบปัญหาที่คล้ายกันซึ่งคุณสมบัติแบบไดนามิกของฉันเป็นเพียงอินสแตนซ์เท่านั้นฉันยังลงเอยด้วยการตั้งค่าแบบคงที่และสิ่งที่ฉันต้องการซึ่งเป็นวัตถุดังนั้นการเริ่มต้นในอนาคตจะใช้ประโยชน์จากพวกเขา โพสต์ของฉันอยู่ด้านล่างและสร้างฟังก์ชั่นผู้ช่วยและวิธีที่ง่ายในการเข้าถึงทุกสิ่งได้อย่างง่ายดาย .x สำหรับคุณสมบัติ, ._x สำหรับข้อมูลดิบที่ getter / setter ใช้ (ซึ่งอาจเป็น None) และ. _x สำหรับวัตถุ accessor
Acecool

57

เป้าหมายคือการสร้างคลาสจำลองซึ่งทำหน้าที่เหมือนชุดผลลัพธ์ db

ดังนั้นสิ่งที่คุณต้องการคือพจนานุกรมที่คุณสามารถสะกด ['b'] ในฐานะ ab?

ง่ายมาก:

class atdict(dict):
    __getattr__= dict.__getitem__
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

1
ในการตั้งค่าทั่วไปเพิ่มเติมนี้มีวัตถุประสงค์ที่ จำกัด หาก dict มีลำดับชั้นหลายระดับเช่น d = {'a1': {'b': 'c'}, 'a2': ... } จากนั้นในขณะที่คุณสามารถทำ d.a1 หรือ d.a2 คุณสามารถ ' t do d.a1.b
Shreyas

1
สิ่งหนึ่งที่ต้องเก็บไว้ในใจก็คือว่านี้จะช่วยให้การตั้งค่าแอตทริบิวต์สำหรับแอตทริบิวต์ที่มีชื่อเดียวกับวิธีการ Dict หรือแอตทริบิวต์ แต่ไม่อนุญาตให้มีการเรียกค่าลักษณะเดียวกันอีกครั้ง: d.items = 1, ผลตอบแทนd.items <built-in method items of atdict object at ...>คุณยังสามารถทำd["items"]หรือใช้__getattribute__แทนได้__getattr__แต่วิธีนี้จะป้องกันการใช้วิธีส่วนใหญ่ของ dict
Marcono1234

เพียงใช้ห้องสมุดแทะเล็ม ! (ทางแยกพวง)
Brian Peterson

38

ดูเหมือนว่าคุณสามารถแก้ปัญหานี้ได้ง่ายกว่าด้วย a namedtupleเนื่องจากคุณรู้รายการของฟิลด์ทั้งหมดล่วงหน้า

from collections import namedtuple

Foo = namedtuple('Foo', ['bar', 'quux'])

foo = Foo(bar=13, quux=74)
print foo.bar, foo.quux

foo2 = Foo()  # error

หากคุณต้องการเขียน setter ของคุณเองคุณจะต้องทำการ metaprogramming ในระดับชั้นเรียน property()ไม่ทำงานบนอินสแตนซ์


ความคิดที่ดี. น่าเสียดายที่ฉันติด python 2.4 อยู่ในขณะนี้
Anthony Kong


2
คนที่เขียนnamedtupleสมควรได้รับรางวัลสำหรับการทำให้มันราบรื่นและสง่างามที่จะเป็นหลักการเชิงวัตถุที่ซื่อสัตย์
Keith Pinson

4
ขออภัยที่ดีที่สุดคำตอบนี้ใช้ได้เฉพาะกับกรณีพิเศษที่หนึ่งคลาส Wanta ประกอบด้วยคุณลักษณะแบบอ่านอย่างเดียวเท่านั้นที่ทุกคนรู้ล่วงหน้า ในคำอื่น ๆ ฉันไม่คิดว่ามันตอบคำถามที่กว้างขึ้นเกี่ยวกับวิธีเพิ่มคุณสมบัติทั่วไป - ไม่ใช่แค่แบบอ่านอย่างเดียว - ไปยังคลาสที่รันไทม์ (หรือเวอร์ชั่นปัจจุบันของคำตอบ "add-on" อื่น ๆ ด้วย โพสต์โดยผู้เขียน)
martineau

@martineau ดังนั้น ... ผ่านการขัดแย้งมากขึ้นproperty()? ไม่มีสิ่งใดในคำตอบที่เฉพาะเจาะจงสำหรับคุณสมบัติแบบอ่านอย่างเดียว
Eevee

32

คุณไม่จำเป็นต้องใช้คุณสมบัติสำหรับสิ่งนั้น เพียงแค่แทนที่__setattr__เพื่อให้พวกเขาอ่านเท่านั้น

class C(object):
    def __init__(self, keys, values):
        for (key, value) in zip(keys, values):
            self.__dict__[key] = value

    def __setattr__(self, name, value):
        raise Exception("It is read only!")

ธาดา

>>> c = C('abc', [1,2,3])
>>> c.a
1
>>> c.b
2
>>> c.c
3
>>> c.d
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute 'd'
>>> c.d = 42
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!
>>> c.a = 'blah'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!

9

วิธีการเพิ่มคุณสมบัติให้คลาสหลามแบบไดนามิก?

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

สร้างชั้นเรียน

การใช้ตัวอย่างตามเอกสารประกอบpropertyให้เราสร้างคลาสของวัตถุด้วยแอตทริบิวต์ "hidden" และสร้างตัวอย่างของมัน

class C(object):
    '''basic class'''
    _x = None

o = C()

ใน Python เราคาดว่าจะมีวิธีหนึ่งที่ชัดเจนในการทำสิ่งต่าง ๆ อย่างไรก็ตามในกรณีนี้ฉันจะแสดงสองวิธีด้วยสัญกรณ์มัณฑนากรและภายนอก ก่อนอื่นโดยไม่มีเครื่องหมายมัณฑนากร สิ่งนี้อาจมีประโยชน์มากกว่าสำหรับการกำหนดแบบไดนามิกของผู้ได้รับผู้ตั้งค่าหรือนักการตลาด

แบบไดนามิก (aka Patching ลิง)

มาสร้างบางอย่างสำหรับชั้นเรียนของเรา:

def getx(self):
    return self._x

def setx(self, value):
    self._x = value

def delx(self):
    del self._x

และตอนนี้เรากำหนดสิ่งเหล่านี้ให้กับอสังหาริมทรัพย์ โปรดทราบว่าเราสามารถเลือกฟังก์ชั่นของเราได้ที่นี่โดยตอบคำถามแบบไดนามิก:

C.x = property(getx, setx, delx, "I'm the 'x' property.")

และการใช้งาน:

>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
>>> help(C.x)
Help on property:

    I'm the 'x' property.

ตกแต่ง

เราสามารถทำเช่นเดียวกับที่เราทำข้างต้นด้วยสัญกรณ์มัณฑนากร แต่ในกรณีนี้เราต้องตั้งชื่อวิธีการทั้งหมดที่มีชื่อเดียวกัน (และฉันขอแนะนำให้เก็บไว้เหมือนกับแอตทริบิวต์) ดังนั้นการมอบหมายทางโปรแกรมจึงไม่สำคัญ มันใช้วิธีการดังกล่าว:

@property
def x(self):
    '''I'm the 'x' property.'''
    return self._x

@x.setter
def x(self, value):
    self._x = value

@x.deleter
def x(self):
    del self._x

และกำหนดออบเจกต์คุณสมบัติด้วย setters และ deleters ที่เตรียมไว้ให้กับคลาส:

C.x = x

และการใช้งาน:

>>> help(C.x)
Help on property:

    I'm the 'x' property.

>>> o.x
>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None

5

ฉันถามคำถามที่คล้ายกันในโพสต์ Stack Overflow นี้เพื่อสร้างโรงงานคลาสซึ่งสร้างประเภทอย่างง่าย ผลลัพธ์คือคำตอบนี้ซึ่งมีเวอร์ชันโรงงานทำงานอยู่ นี่เป็นตัวอย่างของคำตอบ:

def Struct(*args, **kwargs):
    def init(self, *iargs, **ikwargs):
        for k,v in kwargs.items():
            setattr(self, k, v)
        for i in range(len(iargs)):
            setattr(self, args[i], iargs[i])
        for k,v in ikwargs.items():
            setattr(self, k, v)

    name = kwargs.pop("name", "MyStruct")
    kwargs.update(dict((k, None) for k in args))
    return type(name, (object,), {'__init__': init, '__slots__': kwargs.keys()})

>>> Person = Struct('fname', 'age')
>>> person1 = Person('Kevin', 25)
>>> person2 = Person(age=42, fname='Terry')
>>> person1.age += 10
>>> person2.age -= 10
>>> person1.fname, person1.age, person2.fname, person2.age
('Kevin', 35, 'Terry', 32)
>>>

คุณสามารถใช้รูปแบบนี้เพื่อสร้างค่าเริ่มต้นซึ่งเป็นเป้าหมายของคุณ (นอกจากนี้ยังมีคำตอบสำหรับคำถามที่เกี่ยวข้องกับสิ่งนี้)


4

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

class C(object):
    def __init__(self, ks, vs):
        self.__dict__ = dict(zip(ks, vs))


if __name__ == "__main__":
    ks = ['ab', 'cd']
    vs = [12, 34]
    c = C(ks, vs)
    print(c.ab) # 12

ในสาระสำคัญคำถามของฉันคือการหาว่ามันเป็นไปได้ที่จะสร้างสถานที่ให้บริการใหม่ในรันไทม์ ฉันทามติดูเหมือนว่าจะเป็นลบ คำแนะนำของคุณนั้นเรียบง่ายและใช้งานได้จริง (เดียวกันกับคำตอบอื่น ๆ ที่ใช้Dict )
แอนโธนีฮ่องกง

คำตอบง่ายๆก็คือ:self.__dict__[key] = value
Allan Karlson

4

สำหรับผู้ที่มาจากเครื่องมือค้นหานี่คือสองสิ่งที่ฉันกำลังมองหาเมื่อพูดถึงคุณสมบัติแบบไดนามิก :

class Foo:
    def __init__(self):
        # we can dynamically have access to the properties dict using __dict__
        self.__dict__['foo'] = 'bar'

assert Foo().foo == 'bar'


# or we can use __getattr__ and __setattr__ to execute code on set/get
class Bar:
    def __init__(self):
        self._data = {}
    def __getattr__(self, key):
        return self._data[key]
    def __setattr__(self, key, value):
        self._data[key] = value

bar = Bar()
bar.foo = 'bar'
assert bar.foo == 'bar'

__dict__ ดีถ้าคุณต้องการใส่คุณสมบัติที่สร้างขึ้นแบบไดนามิก __getattr__เป็นการดีที่จะทำบางสิ่งเมื่อต้องการค่าเช่นแบบสอบถามฐานข้อมูล คำสั่งผสม / รับเป็นสิ่งที่ดีในการลดความซับซ้อนของการเข้าถึงข้อมูลที่เก็บไว้ในชั้นเรียน

หากคุณต้องการเพียงหนึ่งคุณสมบัติไดนามิกให้ดูที่คุณสมบัติในตัว ()ฟังก์ชั่น


4

คุณไม่สามารถเพิ่มใหม่property()ให้กับอินสแตนซ์ในขณะทำงานได้เนื่องจากคุณสมบัติเป็นตัวอธิบายข้อมูล แต่คุณต้องสร้างคลาสใหม่แบบไดนามิกหรือโอเวอร์โหลด__getattribute__เพื่อประมวลผลตัวให้คำอธิบายข้อมูลในอินสแตนซ์


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

2

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

ks = ['ab', 'cd']
vs = [12, 34]

class C(dict):
    __slots__ = []
    def __init__(self, ks, vs): self.update(zip(ks, vs))
    def __getattr__(self, key): return self[key]

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

ที่พิมพ์ออกมา 12

    c.ab = 33

ที่ให้: AttributeError: 'C' object has no attribute 'ab'


2

เป็นอีกตัวอย่างหนึ่งที่ทำให้ได้ผลตามที่ต้องการ

class Foo(object):

    _bar = None

    @property
    def bar(self):
        return self._bar

    @bar.setter
    def bar(self, value):
        self._bar = value

    def __init__(self, dyn_property_name):
        setattr(Foo, dyn_property_name, Foo.bar)

ดังนั้นตอนนี้เราสามารถทำสิ่งต่าง ๆ เช่น:

>>> foo = Foo('baz')
>>> foo.baz = 5
>>> foo.bar
5
>>> foo.baz
5

2

นี่คือทางออกที่:

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

หลังจากที่มีการกำหนดคลาสคุณเพียงทำเช่นนี้เพื่อเพิ่มคุณสมบัติให้กับมันแบบไดนามิก:

setattr(SomeClass, 'propertyName', property(getter, setter))

นี่คือตัวอย่างที่สมบูรณ์ซึ่งทดสอบใน Python 3:

#!/usr/bin/env python3

class Foo():
  pass

def get_x(self):
  return 3

def set_x(self, value):
  print("set x on %s to %d" % (self, value))

setattr(Foo, 'x', property(get_x, set_x))

foo1 = Foo()
foo1.x = 12
print(foo1.x)

1

คุณสามารถใช้รหัสต่อไปนี้เพื่ออัปเดตแอตทริบิวต์คลาสโดยใช้วัตถุพจนานุกรม:

class ExampleClass():
    def __init__(self, argv):
        for key, val in argv.items():
            self.__dict__[key] = val

if __name__ == '__main__':
    argv = {'intro': 'Hello World!'}
    instance = ExampleClass(argv)
    print instance.intro

1

นี่เป็นสิ่งที่แตกต่างไปเล็กน้อยจากที่ OP ต้องการ แต่ฉันเขย่าสมองของฉันจนกว่าฉันจะได้วิธีแก้ปัญหาการทำงานดังนั้นฉันจึงไปที่นี่เพื่อคนต่อไป / gal

ฉันต้องการวิธีในการระบุ setters และ getters แบบไดนามิก

class X:
    def __init__(self, a=0, b=0, c=0):
        self.a = a
        self.b = b
        self.c = c

    @classmethod
    def _make_properties(cls, field_name, inc):
        _inc = inc

        def _get_properties(self):
            if not hasattr(self, '_%s_inc' % field_name):
                setattr(self, '_%s_inc' % field_name, _inc)
                inc = _inc
            else:
                inc = getattr(self, '_%s_inc' % field_name)

            return getattr(self, field_name) + inc

        def _set_properties(self, value):
            setattr(self, '_%s_inc' % field_name, value)

        return property(_get_properties, _set_properties)

ฉันรู้ว่าทุ่งของฉันก่อนเวลาดังนั้นฉันจะสร้างคุณสมบัติของฉัน หมายเหตุ: คุณไม่สามารถทำเช่นนี้ต่อได้คุณสมบัติเหล่านี้จะมีอยู่ในชั้นเรียน !!!

for inc, field in enumerate(['a', 'b', 'c']):
    setattr(X, '%s_summed' % field, X._make_properties(field, inc))

ลองทดสอบกันเลยตอนนี้ ..

x = X()
assert x.a == 0
assert x.b == 0
assert x.c == 0

assert x.a_summed == 0  # enumerate() set inc to 0 + 0 = 0
assert x.b_summed == 1  # enumerate() set inc to 1 + 0 = 1
assert x.c_summed == 2  # enumerate() set inc to 2 + 0 = 2

# we set the variables to something
x.a = 1
x.b = 2
x.c = 3

assert x.a_summed == 1  # enumerate() set inc to 0 + 1 = 1
assert x.b_summed == 3  # enumerate() set inc to 1 + 2 = 3
assert x.c_summed == 5  # enumerate() set inc to 2 + 3 = 5

# we're changing the inc now
x.a_summed = 1 
x.b_summed = 3 
x.c_summed = 5

assert x.a_summed == 2  # we set inc to 1 + the property was 1 = 2
assert x.b_summed == 5  # we set inc to 3 + the property was 2 = 5
assert x.c_summed == 8  # we set inc to 5 + the property was 3 = 8

มันสับสนไหม? ใช่ขอโทษฉันไม่สามารถมากับตัวอย่างโลกแห่งความหมายใด ๆ ได้ นอกจากนี้สิ่งนี้ไม่ได้มีไว้สำหรับแสงใจ


ถ้าฉันจำได้ถูกต้องฉันหาวิธีในระหว่างการทดสอบทั้งหมดของฉันเพื่อสร้างคุณสมบัติประเภท STATIC / เพิ่ม g / setter แบบไดนามิก ฉันต้องผ่านการตรวจสอบก่อนหน้าทั้งหมด - แต่การเพิ่มบางสิ่งที่ใช้ร่วมกันระหว่างอินสแตนซ์ทั้งหมดเป็นไปได้แน่นอน สำหรับการสร้างตามกระบวนการต่อครั้ง ... ฉันค่อนข้างแน่ใจว่าคุณสามารถทำได้เพื่อให้อินสแตนซ์หนึ่งมีบางสิ่งบางอย่างไม่ได้ ฉันต้องตรวจสอบ แต่ฉันพบบางสิ่งเช่นนี้เช่นกัน (ในความพยายามครั้งแรกของฉันฉันทำผิดพลาดซึ่งทำให้ฟังก์ชั่นถูกสร้างขึ้น แต่ไม่ใช่ทุกกรณีมีเพราะข้อบกพร่อง)
Acecool

นอกจากนี้ยังยินดีต้อนรับทุกทางออกที่เป็นไปได้เพราะนี่คือ repo ของความรู้ นอกจากนี้ยังน่าตื่นเต้นที่ได้เห็นวิธีการที่แตกต่างกันของผู้คนในการสร้างวิธีแก้ไข โซลูชันของฉันทำอะไรมากมายคุณทำสิ่งนี้ลงเพื่อแบ่งปันสิ่งที่ง่ายกว่า ฉันทำสิ่งต่าง ๆ ของฉันที่เล็กลงเช่นกัน - มันควรจะอยู่ที่ไหนสักแห่งในหัวข้อนี้ - และฉันเพิ่งรู้ว่ามันไม่ใช่สิ่งที่ฉันโพสต์: -) ...
Acecool

0

ดูเหมือนว่าจะใช้งานได้ (แต่ดูด้านล่าง):

class data(dict,object):
    def __init__(self,*args,**argd):
        dict.__init__(self,*args,**argd)
        self.__dict__.update(self)
    def __setattr__(self,name,value):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
    def __delattr__(self,name):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)

หากคุณต้องการพฤติกรรมที่ซับซ้อนมากขึ้นอย่าลังเลที่จะแก้ไขคำตอบของคุณ

แก้ไข

ต่อไปนี้อาจจะมีประสิทธิภาพของหน่วยความจำมากขึ้นสำหรับชุดข้อมูลขนาดใหญ่:

class data(dict,object):
    def __init__(self,*args,**argd):
        dict.__init__(self,*args,**argd)
    def __getattr__(self,name):
        return self[name]
    def __setattr__(self,name,value):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
    def __delattr__(self,name):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)

0

ในการตอบคำถามหลักของคุณคุณต้องการแอตทริบิวต์อ่านอย่างเดียวจาก dict เป็นแหล่งข้อมูลที่ไม่เปลี่ยนรูปแบบ:

เป้าหมายคือการสร้างคลาสจำลองซึ่งทำหน้าที่เหมือนชุดผลลัพธ์ db

ตัวอย่างเช่นถ้าเคียวรีฐานข้อมูลส่งคืนโดยใช้นิพจน์ dict {'ab':100, 'cd':200}ฉันก็จะเห็น

>>> dummy.ab
100

ฉันจะสาธิตวิธีใช้ a namedtupleจากcollectionsโมดูลเพื่อทำสิ่งนี้ให้สำเร็จ:

import collections

data = {'ab':100, 'cd':200}

def maketuple(d):
    '''given a dict, return a namedtuple'''
    Tup = collections.namedtuple('TupName', d.keys()) # iterkeys in Python2
    return Tup(**d)

dummy = maketuple(data)
dummy.ab

ผลตอบแทน 100


0
class atdict(dict):
  def __init__(self, value, **kwargs):
    super().__init__(**kwargs)
    self.__dict = value

  def __getattr__(self, name):
    for key in self.__dict:
      if type(self.__dict[key]) is list:
        for idx, item in enumerate(self.__dict[key]):
          if type(item) is dict:
            self.__dict[key][idx] = atdict(item)
      if type(self.__dict[key]) is dict:
        self.__dict[key] = atdict(self.__dict[key])
    return self.__dict[name]



d1 = atdict({'a' : {'b': [{'c': 1}, 2]}})

print(d1.a.b[0].c)

และผลลัพธ์คือ:

>> 1

0

การขยายแนวคิดจากkjfletch

# This is my humble contribution, extending the idea to serialize
# data from and to tuples, comparison operations and allowing functions
# as default values.

def Struct(*args, **kwargs):
    FUNCTIONS = (types.BuiltinFunctionType, types.BuiltinMethodType, \
                 types.FunctionType, types.MethodType)
    def init(self, *iargs, **ikwargs):
        """Asume that unamed args are placed in the same order than
        astuple() yields (currently alphabetic order)
        """
        kw = list(self.__slots__)

        # set the unnamed args
        for i in range(len(iargs)):
            k = kw.pop(0)
            setattr(self, k, iargs[i])

        # set the named args
        for k, v in ikwargs.items():
            setattr(self, k, v)
            kw.remove(k)

        # set default values
        for k in kw:
            v = kwargs[k]
            if isinstance(v, FUNCTIONS):
                v = v()
            setattr(self, k, v)

    def astuple(self):
        return tuple([getattr(self, k) for k in self.__slots__])

    def __str__(self):
        data = ['{}={}'.format(k, getattr(self, k)) for k in self.__slots__]
        return '<{}: {}>'.format(self.__class__.__name__, ', '.join(data))

    def __repr__(self):
        return str(self)

    def __eq__(self, other):
        return self.astuple() == other.astuple()

    name = kwargs.pop("__name__", "MyStruct")
    slots = list(args)
    slots.extend(kwargs.keys())
    # set non-specific default values to None
    kwargs.update(dict((k, None) for k in args))

    return type(name, (object,), {
        '__init__': init,
        '__slots__': tuple(slots),
        'astuple': astuple,
        '__str__': __str__,
        '__repr__': __repr__,
        '__eq__': __eq__,
    })


Event = Struct('user', 'cmd', \
               'arg1', 'arg2',  \
               date=time.time, \
               __name__='Event')

aa = Event('pepe', 77)
print(aa)
raw = aa.astuple()

bb = Event(*raw)
print(bb)

if aa == bb:
    print('Are equals')

cc = Event(cmd='foo')
print(cc)

เอาท์พุท:

<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
Are equals
<Event: user=None, cmd=foo, arg1=None, arg2=None, date=1550051403.7938335>

0

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

#!/usr/local/bin/python3

INITS = { 'ab': 100, 'cd': 200 }

class DP(dict):
  def __init__(self):
    super().__init__()
    for k,v in INITS.items():
        self[k] = v 

def _dict_set(dp, key, value):
  dp[key] = value

for item in INITS.keys():
  setattr(
    DP,
    item,
    lambda key: property(
      lambda self: self[key], lambda self, value: _dict_set(self, key, value)
    )(item)
  )

a = DP()
print(a)  # {'ab': 100, 'cd': 200}
a.ab = 'ab100'
a.cd = False
print(a.ab, a.cd) # ab100 False

0

สิ่งที่เหมาะกับฉันคือ:

class C:
    def __init__(self):
        self._x=None

    def g(self):
        return self._x

    def s(self, x):
        self._x = x

    def d(self):
        del self._x

    def s2(self,x):
        self._x=x+x

    x=property(g,s,d)


c = C()
c.x="a"
print(c.x)

C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x)

เอาท์พุต

a
aa

-1

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

class C(object):
    def __init__(self, properties):
        self.existing = "Still Here"
        self.properties = properties

    def __getattr__(self, name):
        if "properties" in self.__dict__ and name in self.properties:
            return self.properties[name] # Or call a function, etc
        return self.__dict__[name]

    def __setattr__(self, name, value):
        if "properties" in self.__dict__ and name in self.properties:
            self.properties[name] = value
        else:
            self.__dict__[name] = value

if __name__ == "__main__":
    my_properties = {'a':1, 'b':2, 'c':3}
    c = C(my_properties)
    assert c.a == 1
    assert c.existing == "Still Here"
    c.b = 10
    assert c.properties['b'] == 10

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

เช่น: ฉันจะต้องสร้างคลาสพื้นฐานที่ฉันขยายลูกของฉันทั้งหมดที่ใช้ระบบจากหรือฉันต้องเพิ่มฟังก์ชั่นมายากล s / getattr ให้กับทุกสิ่งและทำซ้ำระบบทุกครั้ง ประกาศของคุณสมบัติยังหมายความว่าคุณต้องตั้งค่าพวกเขาทางเดียวและถ้าคุณต้องการการสนับสนุนเพิ่มเติมใด ๆ ตามที่ฉันได้ระบุไว้เช่นชนิดข้อมูลและการป้องกันค่าหรือเพื่ออนุญาตหรือป้องกันข้อมูลจากการได้รับมอบหมายและผู้ช่วยอื่น ๆ จากนั้นคุณต้องเขียนโค้ดเหล่านี้ค่ะคุณสามารถทำให้ระบบมีลักษณะการทำงานคล้ายกัน แต่ท้ายที่สุดคุณก็ประกาศว่าแตกต่างกันและมีขนาดใหญ่ขึ้นเล็กน้อย
Acecool

-1

นี่คือตัวอย่างง่าย ๆ ในการสร้างวัตถุคุณสมบัติโดยทางโปรแกรม

#!/usr/bin/python3

class Counter:
    def __init__(self):
        cls = self.__class__
        self._count = 0
        cls.count = self.count_ref()

    def count_get(self):
        print(f'count_get: {self._count}')
        return self._count

    def count_set(self, value):
        self._count = value
        print(f'count_set: {self._count}')

    def count_del(self):
        print(f'count_del: {self._count}')

    def count_ref(self):
        cls = self.__class__
        return property(fget=cls.count_get, fset=cls.count_set, fdel=cls.count_del)

counter = Counter()

counter.count
for i in range(5):
    counter.count = i
del counter.count

'''
output
======
count_get: 0
count_set: 0
count_set: 1
count_set: 2
count_set: 3
count_set: 4
count_del: 4
'''

-2

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

class Holder: p = property(lambda x: vs[i], self.fn_readonly)
setattr(self, k, Holder().p)

1
ดูเหมือนว่าจะใช้งานไม่ได้ มันจะกำหนดผลลัพธ์ของคุณสมบัติไม่ใช่คุณสมบัติของมันเอง
mjallday

สิ่งนี้ไม่ถูกต้อง ฉันแนบคุณสมบัติแบบไดนามิกกับระบบของฉันโดยไม่ต้องเริ่มต้นชั้นเรียน เริ่มต้นเป็น x = ตัวอย่าง () จากนั้นเพิ่มคุณสมบัติให้กับ x
Acecool

หากคุณดูรหัสของฉันคุณจะเห็นว่าฉันใช้คลาส ExampleBase: pass จากนั้นคลาสตัวอย่าง (ExampleBase): ... จากนั้นฉันจะแนบคุณสมบัติกับ ExampleBase เพราะชื่อนั้นมีอยู่แล้วและเนื่องจากตัวอย่างขยายจากนั้น มันสามารถเข้าถึงทุกสิ่ง ฉันใช้ __ var สำหรับตัวช่วยการเข้าถึงเพื่อให้สามารถเข้าถึงวัตถุ accessor ได้โดยตรงฉันใช้ _ สำหรับข้อมูลที่เก็บไว้ (ดิบ) ซึ่งอาจเป็น None และไม่มีขีดเส้นใต้สำหรับคุณสมบัติที่แท้จริงซึ่งผ่านทะลวง ฉันสามารถเรียกใช้ฟังก์ชัน getter โดยใช้ฟังก์ชันที่เพิ่มเข้ามาแบบไดนามิกหรือใช้คุณสมบัติ ทั้งหมดโดยไม่ต้องเริ่มต้นครั้งแรก
Acecool

หมายเหตุ: ฉันพูดถึงมัน - แต่คำจำกัดความของฉันสำหรับคำนิยามหมายถึงการอ้างอิงที่มีอยู่ในเนมสเปซ - เช่น: คลาสตัวอย่าง (Object): ผ่าน ... มีอยู่ แต่ยังไม่ได้เริ่มต้น การเริ่มต้นหมายถึง blah = ตัวอย่าง (); ตอนนี้วัตถุถูกทำซ้ำและสร้างขึ้นจากนั้นเก็บไว้เป็นข้อมูลอ้างอิงใน blah --- ถ้าคุณทำเช่นนี้ฟังก์ชั่น / คุณสมบัติที่เพิ่มเข้ามานั้นควรจะอยู่ในอินสแตนซ์เท่านั้น - ปัญหาที่ฉันมีกับเรื่องนี้คือแม้ว่าจะมีฟังก์ชั่นอยู่แล้วก็ตามมีบางกรณีที่ฉันพบข้อผิดพลาด การบล็อกข้อผิดพลาดหยุดการสร้างหรือการดำเนินการ async
Acecool

หมายเหตุอื่น ๆ : มันเป็นไปได้ที่จะสร้างคุณสมบัติแบบไดนามิกและฟังก์ชั่นในลักษณะที่มีอยู่ต่ออินสแตนซ์เท่านั้น คุณสามารถทำให้มันมีอยู่เพื่อวัตถุ (ซึ่งเป็นสิ่งที่คุณต้องการในกรณีส่วนใหญ่) และมีบางกรณีที่องค์ประกอบที่เพิ่มเป็น 'คงที่' คือการอ้างอิงเดียวกันและค่าที่ส่งคืนจะถูกใช้ร่วมกันในทุกกรณี - หากคุณอัปเดตในพื้นที่หนึ่งทั้งหมดจะได้รับเหมือนกัน ..
Acecool

-6

คำตอบที่ให้มาจำนวนมากต้องการบรรทัดจำนวนมากต่อหนึ่งคุณสมบัติเช่น / และ / หรือ - สิ่งที่ฉันพิจารณาการใช้งานที่น่าเกลียดหรือน่าเบื่อเพราะการทำซ้ำที่จำเป็นสำหรับคุณสมบัติหลาย ๆ แบบ ฯลฯ ฉันชอบที่จะทำให้สิ่งต่าง ๆ เดือดลง ไม่สามารถทำให้ง่ายขึ้นได้อีกต่อไปหรือจนกว่ามันจะไม่ได้มีจุดประสงค์มากมายในการทำเช่นนั้น

กล่าวโดยย่อ: ในงานที่เสร็จสมบูรณ์ถ้าฉันทำซ้ำรหัส 2 บรรทัดฉันมักจะแปลงเป็นฟังก์ชันตัวช่วยบรรทัดเดียวและอื่น ๆ ... ฉันลดความซับซ้อนของคณิตศาสตร์หรือข้อโต้แย้งแปลก ๆ เช่น (start_x, start_y, end_x, end_y) เป็น (x, y, w, h) เช่น x, y, x + w, y + h (บางครั้งต้องการ min / max หรือถ้า w / h เป็นค่าลบและการนำไปใช้ไม่ชอบมันฉันจะลบจาก x / y และ abs w / h. ฯลฯ .. )

การเอาชนะ getters / setters ภายในเป็นวิธีที่ดีที่จะไป แต่ปัญหาคือคุณต้องทำเช่นนั้นสำหรับทุกชั้นเรียนหรือผู้ปกครองชั้นเรียนไปยังฐานนั้น ... มันไม่ได้ผลสำหรับฉันอย่างที่ฉันอยากจะเป็น อิสระในการเลือกเด็ก / ผู้ปกครองสำหรับการสืบทอดโหนดลูก ฯลฯ

ฉันได้สร้างโซลูชันที่ตอบคำถามโดยไม่ใช้ Dict data-type เพื่อให้ข้อมูลเพราะฉันพบว่ามันน่าเบื่อที่จะป้อนข้อมูล ฯลฯ ...

โซลูชันของฉันต้องการให้คุณเพิ่ม 2 บรรทัดพิเศษเหนือคลาสของคุณเพื่อสร้างคลาสพื้นฐานสำหรับคลาสที่คุณต้องการเพิ่มคุณสมบัติไปแล้ว 1 บรรทัดต่อและคุณมีตัวเลือกในการเพิ่มการโทรกลับเพื่อควบคุมข้อมูลแจ้งให้คุณทราบเมื่อมีการเปลี่ยนแปลงข้อมูล จำกัด ข้อมูลที่สามารถตั้งค่าตามค่าและ / หรือประเภทข้อมูลและอื่น ๆ อีกมากมาย

คุณยังมีตัวเลือกในการใช้ _object.x, _object.x = value, _object.GetX (), _object.SetX (ค่า) และพวกเขาจะได้รับการจัดการอย่างเท่าเทียมกัน

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

มีฟังก์ชั่นตัวช่วยมากมายเช่นกัน - คุณสมบัติแรกที่เพิ่มเข้ามาจะเพิ่มผู้ช่วย 2 คนเพื่ออ้างอิงค่าอินสแตนซ์ ... พวกเขาคือ ResetAccessors (_key, .. ) varargs ซ้ำ (ทั้งหมดสามารถทำซ้ำได้โดยใช้ args ชื่อแรก ) และ SetAccessors (_key, _value) พร้อมตัวเลือกที่เพิ่มเข้ามาในคลาสหลักเพื่อช่วยในการเพิ่มประสิทธิภาพ - สิ่งที่วางแผนไว้คือ: วิธีการรวมกลุ่ม accessors เข้าด้วยกันดังนั้นหากคุณมักจะรีเซ็ตทีละครั้งทุกครั้ง คุณสามารถกำหนดให้กับกลุ่มและรีเซ็ตกลุ่มแทนการทำซ้ำคีย์ที่ตั้งชื่อในแต่ละครั้งและอีกมากมาย

ค่าที่เก็บไว้ของอินสแตนซ์ / ดิบถูกเก็บไว้ที่คลาส, ห้องเรียน. อ้างอิง Accessor Class ซึ่งเก็บ vars / values ​​/ function แบบคงที่สำหรับคุณสมบัติ _class เป็นคุณสมบัติที่เรียกว่าเมื่อเข้าถึงผ่านคลาสอินสแตนซ์ระหว่างการตั้งค่า / การรับเป็นต้น

Accessor _class .__ ชี้ไปที่คลาส แต่เนื่องจากภายในจะต้องมีการกำหนดในคลาสซึ่งเป็นสาเหตุที่ฉันเลือกที่จะใช้ __Name = AccessorFunc (... ) เพื่อกำหนดมันบรรทัดเดียวต่อคุณสมบัติที่มีตัวเลือกมากมาย ข้อโต้แย้งที่จะใช้ (ใช้ varargs ที่สำคัญเนื่องจากง่ายและมีประสิทธิภาพมากกว่าในการระบุและดูแล)

ฉันยังสร้างฟังก์ชั่นจำนวนมากตามที่กล่าวไว้บางส่วนใช้ข้อมูลฟังก์ชั่นการเข้าถึงดังนั้นจึงไม่จำเป็นต้องถูกเรียก (เพราะมันค่อนข้างไม่สะดวกในขณะนี้ - ตอนนี้คุณต้องใช้ _class. .FunctionName (_class_instance) , args) - ฉันได้ใช้สแต็ค / ติดตามเพื่อคว้าการอ้างอิงอินสแตนซ์เพื่อคว้าค่าโดยการเพิ่มฟังก์ชั่นที่เรียกใช้บิตมาราธอนนี้หรือโดยการเพิ่ม accessors ไปยังวัตถุและใช้ตัวเอง สำหรับอินสแตนซ์และเพื่อรักษาการเข้าถึงตนเองการอ้างอิงคลาส AccessorFunc และข้อมูลอื่น ๆ จากภายในนิยามฟังก์ชัน)

มันไม่ได้ทำค่อนข้าง แต่มันถือเท้าที่ยอดเยี่ยม หมายเหตุ: หากคุณไม่ได้ใช้ __Name = AccessorFunc (... ) เพื่อสร้างคุณสมบัติคุณจะไม่สามารถเข้าถึงคีย์ __ แม้ว่าฉันจะกำหนดภายในฟังก์ชัน init ถ้าคุณทำแล้วไม่มีปัญหา

นอกจากนี้โปรดทราบว่าชื่อและคีย์นั้นแตกต่างกัน ... ชื่อคือ 'เป็นทางการ' ซึ่งใช้ในการสร้างชื่อฟังก์ชั่นและที่สำคัญคือสำหรับการจัดเก็บข้อมูลและการเข้าถึง ie _class.x โดยที่ตัวพิมพ์เล็ก x เป็นคีย์ชื่อจะเป็นตัวพิมพ์ใหญ่ X ดังนั้น GetX () เป็นฟังก์ชันแทนที่จะเป็น Getx () ซึ่งดูแปลก ๆ เล็กน้อย สิ่งนี้ทำให้ self.x ทำงานและดูเหมาะสม แต่ยังอนุญาตให้ GetX () และดูเหมาะสม

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

รายการฟังก์ชั่นปัจจุบันโดยใช้คีย์: x, ชื่อ: X เอาต์พุตเป็น:

นี่ไม่ใช่รายการที่ครอบคลุม - มีบางอย่างที่ยังไม่ได้ทำในตอนที่โพสต์ ...

_instance.SetAccessors( _key, _value [ , _key, _value ] .. )                   Instance Class Helper Function: Allows assigning many keys / values on a single line - useful for initial setup, or to minimize lines.    In short: Calls this.Set<Name>( _value ) for each _key / _value pairing.
_instance.ResetAccessors( _key [ , _key ] .. )                                 Instance Class Helper Function: Allows resetting many key stored values to None on a single line.                                           In short: Calls this.Reset<Name>() for each name provided.


Note: Functions below may list self.Get / Set / Name( _args ) - self is meant as the class instance reference in the cases below - coded as this in AccessorFuncBase Class.

this.GetX( _default_override = None, _ignore_defaults = False )                 GET:            Returns    IF ISSET: STORED_VALUE .. IF IGNORE_DEFAULTS: None  .. IF PROVIDED: DEFAULT_OVERRIDE ELSE: DEFAULT_VALUE       100
this.GetXRaw( )                                                                 RAW:            Returns    STORED_VALUE                                                                                                     100
this.IsXSet( )                                                                  ISSET:          Returns    ( STORED_VALUE != None )                                                                                         True

this.GetXToString( )                                                            GETSTR:         Returns    str( GET )                                                                                                       100
this.GetXLen( _default_override = None, _ignore_defaults = False )              LEN:            Returns    len( GET )                                                                                                       3
this.GetXLenToString( _default_override = None, _ignore_defaults = False )      LENSTR:         Returns    str( len( GET ) )                                                                                                3
this.GetXDefaultValue( )                                                        DEFAULT:        Returns    DEFAULT_VALUE                                                                                                    1111

this.GetXAccessor( )                                                            ACCESSOR:       Returns    ACCESSOR_REF ( self.__<key> )                                                                                    [ AccessorFuncBase ] Key: x : Class ID: 2231452344344 : self ID: 2231448283848        Default: 1111       Allowed Types: {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}     Allowed Values: None
this.GetXAllowedTypes( )                                                        ALLOWED_TYPES:  Returns    Allowed Data-Types                                                                                               {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}
this.GetXAllowedValues( )                                                       ALLOWED_VALUES: Returns    Allowed Values                                                                                                   None

this.GetXHelpers( )                                                             HELPERS:        Returns    Helper Functions String List - ie what you're reading now...                                                     THESE ROWS OF TEXT
this.GetXKeyOutput( )                                                           Returns information about this Name / Key                                                                                                   ROWS OF TEXT
this.GetXGetterOutput( )                                                        Returns information about this Name / Key                                                                                                   ROWS OF TEXT

this.SetX( _value )                                                             SET:            STORED_VALUE Setter - ie Redirect to __<Key>.Set                                                                            N / A
this.ResetX( )                                                                  RESET:          Resets STORED_VALUE to None                                                                                                 N / A

this.HasXGetterPrefix( )                                                        Returns Whether or Not this key has a Getter Prefix...                                                                                      True
this.GetXGetterPrefix( )                                                        Returns Getter Prefix...                                                                                                                    Get

this.GetXName( )                                                                Returns Accessor Name - Typically Formal / Title-Case                                                                                       X
this.GetXKey( )                                                                 Returns Accessor Property Key - Typically Lower-Case                                                                                        x
this.GetXAccessorKey( )                                                         Returns Accessor Key - This is to access internal functions, and static data...                                                             __x
this.GetXDataKey( )                                                             Returns Accessor Data-Storage Key - This is the location where the class instance value is stored..                                         _x

ข้อมูลที่ส่งออกบางส่วนคือ:

นี่คือคลาสใหม่เอี่ยมที่สร้างขึ้นโดยใช้คลาสการสาธิตโดยไม่มีข้อมูลใด ๆ ที่ได้รับมอบหมายนอกเหนือจากชื่อ (เพื่อให้สามารถส่งออก) ซึ่งเป็น _foo, ชื่อตัวแปรที่ฉันใช้ ...

_foo         --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016

    Key       Getter Value        | Raw Key   Raw / Stored Value       | Get Default Value             Default Value            | Get Allowed Types             Allowed Types                                                              | Get Allowed Values            Allowed Values                                                                                                                                                                                                                   |

    Name:     _foo                | _Name:    _foo                     | __Name.DefaultValue( ):       AccessorFuncDemoClass    | __Name.GetAllowedTypes( )     <class 'str'>                                                              | __Name.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    x:        1111                | _x:       None                     | __x.DefaultValue( ):          1111                     | __x.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __x.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    y:        2222                | _y:       None                     | __y.DefaultValue( ):          2222                     | __y.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __y.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    z:        3333                | _z:       None                     | __z.DefaultValue( ):          3333                     | __z.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __z.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Blah:     <class 'int'>       | _Blah:    None                     | __Blah.DefaultValue( ):       <class 'int'>            | __Blah.GetAllowedTypes( )     <class 'str'>                                                              | __Blah.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Width:    1                   | _Width:   None                     | __Width.DefaultValue( ):      1                        | __Width.GetAllowedTypes( )    (<class 'int'>, <class 'bool'>)                                            | __Width.GetAllowedValues( )   Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Height:   0                   | _Height:  None                     | __Height.DefaultValue( ):     0                        | __Height.GetAllowedTypes( )   <class 'int'>                                                              | __Height.GetAllowedValues( )  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |
    Depth:    2                   | _Depth:   None                     | __Depth.DefaultValue( ):      2                        | __Depth.GetAllowedTypes( )    Saved Value Restricted to Authorized Values ONLY                           | __Depth.GetAllowedValues( )   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |


this.IsNameSet( ):    True      this.GetName( ):     _foo                     this.GetNameRaw( ):    _foo                     this.GetNameDefaultValue( ):    AccessorFuncDemoClass    this.GetNameLen( ):    4    this.HasNameGetterPrefix( ):    <class 'str'>                                this.GetNameGetterPrefix( ):    None
this.IsXSet( ):       False     this.GetX( ):        1111                     this.GetXRaw( ):       None                     this.GetXDefaultValue( ):       1111                     this.GetXLen( ):       4    this.HasXGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetXGetterPrefix( ):       None
this.IsYSet( ):       False     this.GetY( ):        2222                     this.GetYRaw( ):       None                     this.GetYDefaultValue( ):       2222                     this.GetYLen( ):       4    this.HasYGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetYGetterPrefix( ):       None
this.IsZSet( ):       False     this.GetZ( ):        3333                     this.GetZRaw( ):       None                     this.GetZDefaultValue( ):       3333                     this.GetZLen( ):       4    this.HasZGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetZGetterPrefix( ):       None
this.IsBlahSet( ):    False     this.GetBlah( ):     <class 'int'>            this.GetBlahRaw( ):    None                     this.GetBlahDefaultValue( ):    <class 'int'>            this.GetBlahLen( ):    13   this.HasBlahGetterPrefix( ):    <class 'str'>                                this.GetBlahGetterPrefix( ):    None
this.IsWidthSet( ):   False     this.GetWidth( ):    1                        this.GetWidthRaw( ):   None                     this.GetWidthDefaultValue( ):   1                        this.GetWidthLen( ):   1    this.HasWidthGetterPrefix( ):   (<class 'int'>, <class 'bool'>)              this.GetWidthGetterPrefix( ):   None
this.IsDepthSet( ):   False     this.GetDepth( ):    2                        this.GetDepthRaw( ):   None                     this.GetDepthDefaultValue( ):   2                        this.GetDepthLen( ):   1    this.HasDepthGetterPrefix( ):   None                                         this.GetDepthGetterPrefix( ):   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ):  False     this.GetHeight( ):   0                        this.GetHeightRaw( ):  None                     this.GetHeightDefaultValue( ):  0                        this.GetHeightLen( ):  1    this.HasHeightGetterPrefix( ):  <class 'int'>                                this.GetHeightGetterPrefix( ):  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

และนี่คือหลังจากกำหนดคุณสมบัติ _foo ทั้งหมด (ยกเว้นชื่อ) ค่าต่อไปนี้ในลำดับเดียวกัน: 'string', 1.0, True, 9, 10, False

this.IsNameSet( ):    True      this.GetName( ):     _foo                     this.GetNameRaw( ):    _foo                     this.GetNameDefaultValue( ):    AccessorFuncDemoClass    this.GetNameLen( ):    4    this.HasNameGetterPrefix( ):    <class 'str'>                                this.GetNameGetterPrefix( ):    None
this.IsXSet( ):       True      this.GetX( ):        10                       this.GetXRaw( ):       10                       this.GetXDefaultValue( ):       1111                     this.GetXLen( ):       2    this.HasXGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetXGetterPrefix( ):       None
this.IsYSet( ):       True      this.GetY( ):        10                       this.GetYRaw( ):       10                       this.GetYDefaultValue( ):       2222                     this.GetYLen( ):       2    this.HasYGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetYGetterPrefix( ):       None
this.IsZSet( ):       True      this.GetZ( ):        10                       this.GetZRaw( ):       10                       this.GetZDefaultValue( ):       3333                     this.GetZLen( ):       2    this.HasZGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetZGetterPrefix( ):       None
this.IsBlahSet( ):    True      this.GetBlah( ):     string Blah              this.GetBlahRaw( ):    string Blah              this.GetBlahDefaultValue( ):    <class 'int'>            this.GetBlahLen( ):    11   this.HasBlahGetterPrefix( ):    <class 'str'>                                this.GetBlahGetterPrefix( ):    None
this.IsWidthSet( ):   True      this.GetWidth( ):    False                    this.GetWidthRaw( ):   False                    this.GetWidthDefaultValue( ):   1                        this.GetWidthLen( ):   5    this.HasWidthGetterPrefix( ):   (<class 'int'>, <class 'bool'>)              this.GetWidthGetterPrefix( ):   None
this.IsDepthSet( ):   True      this.GetDepth( ):    9                        this.GetDepthRaw( ):   9                        this.GetDepthDefaultValue( ):   2                        this.GetDepthLen( ):   1    this.HasDepthGetterPrefix( ):   None                                         this.GetDepthGetterPrefix( ):   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ):  True      this.GetHeight( ):   9                        this.GetHeightRaw( ):  9                        this.GetHeightDefaultValue( ):  0                        this.GetHeightLen( ):  1    this.HasHeightGetterPrefix( ):  <class 'int'>                                this.GetHeightGetterPrefix( ):  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

_foo         --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016

    Key       Getter Value        | Raw Key   Raw / Stored Value       | Get Default Value             Default Value            | Get Allowed Types             Allowed Types                                                              | Get Allowed Values            Allowed Values                                                                                                                                                                                                                   |

    Name:     _foo                | _Name:    _foo                     | __Name.DefaultValue( ):       AccessorFuncDemoClass    | __Name.GetAllowedTypes( )     <class 'str'>                                                              | __Name.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    x:        10                  | _x:       10                       | __x.DefaultValue( ):          1111                     | __x.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __x.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    y:        10                  | _y:       10                       | __y.DefaultValue( ):          2222                     | __y.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __y.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    z:        10                  | _z:       10                       | __z.DefaultValue( ):          3333                     | __z.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __z.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Blah:     string Blah         | _Blah:    string Blah              | __Blah.DefaultValue( ):       <class 'int'>            | __Blah.GetAllowedTypes( )     <class 'str'>                                                              | __Blah.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Width:    False               | _Width:   False                    | __Width.DefaultValue( ):      1                        | __Width.GetAllowedTypes( )    (<class 'int'>, <class 'bool'>)                                            | __Width.GetAllowedValues( )   Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Height:   9                   | _Height:  9                        | __Height.DefaultValue( ):     0                        | __Height.GetAllowedTypes( )   <class 'int'>                                                              | __Height.GetAllowedValues( )  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |
    Depth:    9                   | _Depth:   9                        | __Depth.DefaultValue( ):      2                        | __Depth.GetAllowedTypes( )    Saved Value Restricted to Authorized Values ONLY                           | __Depth.GetAllowedValues( )   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |

โปรดทราบว่าเนื่องจากข้อ จำกัด ชนิดข้อมูลหรือค่า จำกัด ข้อมูลบางอย่างไม่ได้ถูกกำหนด - นี่คือโดยการออกแบบ setter ห้ามมิให้กำหนดชนิดข้อมูลหรือค่าที่ไม่ถูกต้องแม้จะถูกกำหนดเป็นค่าเริ่มต้น (เว้นแต่คุณจะแทนที่พฤติกรรมการป้องกันค่าเริ่มต้น)

รหัสไม่ได้ถูกโพสต์ที่นี่เพราะฉันไม่มีห้องว่างหลังจากตัวอย่างและคำอธิบาย ... และยังเพราะมันจะเปลี่ยน

โปรดทราบ: ในขณะที่ทำการโพสต์นี้ไฟล์นั้นยุ่ง - สิ่งนี้จะเปลี่ยนไป แต่ถ้าคุณเรียกใช้ใน Sublime Text และคอมไพล์หรือเรียกใช้จาก Python มันจะรวบรวมและคายข้อมูลออกมามากมาย - ส่วน AccessorDB ไม่เสร็จสมบูรณ์ (ซึ่งจะใช้ในการอัพเดท Print Getters และ GetKeyOutput helper ฟังก์ชั่นพร้อมกับการเปลี่ยนเป็นฟังก์ชั่นอินสแตนซ์อาจใส่ในฟังก์ชั่นเดียวและเปลี่ยนชื่อ - มองหา .. )

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

ฉันกำลังมองหาวิธีแก้ไขเพื่อต้องการ MyClassBase: pass, MyClass (MyClassBase): ... - ถ้าคุณรู้วิธีแก้ปัญหา - โพสต์ไว้

สิ่งเดียวที่จำเป็นในชั้นเรียนคือ __ บรรทัด - str ใช้สำหรับการดีบั๊กเช่นเดียวกับinit - สามารถลบออกจาก Demo Class ได้ แต่คุณจะต้องแสดงความคิดเห็นหรือลบบรรทัดด้านล่าง (_foo / 2/3) ) ..

คลาส String, Dict และ Util ที่ด้านบนเป็นส่วนหนึ่งของ Python library ของฉัน - มันยังไม่เสร็จสมบูรณ์ ฉันคัดลอกบางสิ่งที่ฉันต้องการจากห้องสมุดและฉันสร้างสิ่งใหม่สองสามอย่าง รหัสเต็มจะเชื่อมโยงไปยังห้องสมุดที่สมบูรณ์และจะรวมกับการโทรที่มีการปรับปรุงและลบรหัส (อันที่จริงรหัสเดียวที่เหลือจะเป็นคลาสการสาธิตและคำสั่งการพิมพ์ - ระบบ AccessorFunc จะถูกย้ายไปที่ห้องสมุด) ..

ส่วนหนึ่งของไฟล์:

##
## MyClass Test AccessorFunc Implementation for Dynamic 1-line Parameters
##
class AccessorFuncDemoClassBase( ):
    pass
class AccessorFuncDemoClass( AccessorFuncDemoClassBase ):
    __Name      = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Name',      default = 'AccessorFuncDemoClass',  allowed_types = ( TYPE_STRING ),                    allowed_values = VALUE_ANY,                 documentation = 'Name Docs',        getter_prefix = 'Get',  key = 'Name',       allow_erroneous_default = False,    options = { } )
    __x         = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'X',         default = 1111,                     allowed_types = ( TYPE_INTEGER, TYPE_FLOAT ),       allowed_values = VALUE_ANY,                 documentation = 'X Docs',           getter_prefix = 'Get',  key = 'x',          allow_erroneous_default = False,    options = { } )
    __Height    = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Height',    default = 0,                        allowed_types = TYPE_INTEGER,                       allowed_values = VALUE_SINGLE_DIGITS,       documentation = 'Height Docs',      getter_prefix = 'Get',  key = 'Height',     allow_erroneous_default = False,    options = { } )

ความงามนี้ทำให้การสร้างคลาสใหม่ง่ายขึ้นอย่างเหลือเชื่อด้วยคุณสมบัติที่เพิ่มขึ้นแบบไดนามิกด้วยการบังคับใช้ AccessorFuncs / callbacks / data-type / value เป็นต้น

สำหรับตอนนี้ลิงก์อยู่ที่ (ลิงก์นี้ควรแสดงถึงการเปลี่ยนแปลงในเอกสาร): https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0

นอกจากนี้: หากคุณไม่ได้ใช้ Sublime Text ฉันขอแนะนำให้ใช้มากกว่า Notepad ++, Atom, Visual Code และอื่น ๆ เพราะการใช้เธรดที่เหมาะสมทำให้ใช้งานได้เร็วขึ้นมาก ... ฉันกำลังใช้โค้ด IDE เหมือนกัน ระบบการทำแผนที่ - ลองดูที่: https://bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/ (เพิ่ม Repo ใน Package Manager ก่อนจากนั้นติดตั้งปลั๊กอิน - เมื่อเวอร์ชัน 1.0.0 พร้อมฉันจะเพิ่ม ไปยังรายการปลั๊กอินหลัก ... )

ฉันหวังว่าโซลูชันนี้จะช่วย ... และเช่นเคย:

เพียงเพราะมันใช้งานได้ไม่ถูกต้อง - โมเซอร์ Josh 'Acecool'


ฉันต้องการที่จะเพิ่มการแสดงผลที่รวดเร็วของสิ่งที่ชั้นดูเหมือนดังนั้นคุณจึงไม่จำเป็นต้องเปิดไฟล์โค้ด แต่ความเห็นดูเหมือนจะไม่สนับสนุนมัน ..
Acecool

เห็นได้ชัดว่านี่กำลังได้รับความเกลียดชังมากมายซึ่งทำให้เกิดความสับสน มันทำสิ่งที่ OP ต้องการอย่างแท้จริง - เพิ่มคุณสมบัติให้กับวัตถุแบบไดนามิก นอกจากนี้ยังเพิ่มฟังก์ชั่นตัวช่วยซึ่งไม่จำเป็นต้องรวม - อาจเป็นสาเหตุที่ทำให้เกิดความเกลียดชัง - และยังทำให้แน่ใจว่าผู้พัฒนามีวิธีที่ง่ายในการเข้าถึงพร็อพเพอร์ตี้ (.x) ซึ่งประมวลผลผ่านทะเยอทะยาน ค่า raw เก็บไว้ (._x) ซึ่งสามารถเป็น None เมื่อ. x คืนค่าเริ่มต้นหรืออย่างอื่นและวิธีเข้าถึง accessor เพื่อใช้ผู้ช่วยเปลี่ยนสิ่งต่าง ๆ ฯลฯ (.__ x) ....
Acecool
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.