ตัวแปรคลาสแบบคงที่เป็นไปได้ใน Python หรือไม่


คำตอบ:


1900

ตัวแปรที่ประกาศภายในนิยามคลาส แต่ไม่ใช่ภายในเมธอดคือตัวแปรคลาสหรือสแตติก:

>>> class MyClass:
...     i = 3
...
>>> MyClass.i
3 

เมื่อ @ millerdevชี้ให้เห็นสิ่งนี้จะสร้างiตัวแปรระดับคลาสแต่สิ่งนี้แตกต่างจากiตัวแปรระดับอินสแตนซ์ดังนั้นคุณอาจมี

>>> m = MyClass()
>>> m.i = 4
>>> MyClass.i, m.i
>>> (3, 4)

สิ่งนี้แตกต่างจาก C ++ และ Java แต่ไม่แตกต่างจาก C # ที่สมาชิกแบบสแตติกไม่สามารถเข้าถึงได้โดยใช้การอ้างอิงไปยังอินสแตนซ์

ดูว่าบทสอนของ Python เกี่ยวกับเรื่องของคลาสและออบเจ็กต์ของชั้นเรียนมีอะไรบ้าง

@ Steve จอห์นสันมีคำตอบอยู่แล้วเกี่ยวกับวิธีการคงยังเอกสารภายใต้"Built-in ฟังก์ชั่น" ในการอ้างอิงไลบรารีหลาม

class C:
    @staticmethod
    def f(arg1, arg2, ...): ...

@beidy แนะนำclassmethod s มากกว่า staticmethod เนื่องจากวิธีการนั้นได้รับประเภท class เป็นอาร์กิวเมนต์แรก แต่ฉันยังคงคลุมเครือเล็กน้อยเกี่ยวกับข้อดีของวิธีการนี้มากกว่า staticmethod หากคุณเป็นเช่นนั้นก็คงไม่เป็นไร


11
ฉันแค่เรียนรู้ Python แต่ข้อดีของAFAIK ที่@classmethodเหนือกว่า@staticmethodคือคุณจะได้ชื่อของคลาสที่มีการเรียกใช้เมธอดถึงแม้ว่ามันจะเป็นคลาสย่อยก็ตาม วิธีการแบบสแตติกขาดข้อมูลนี้ดังนั้นจึงไม่สามารถเรียกวิธีการแทนที่ได้ตัวอย่างเช่น
Seb

49
@TheJollySin วิธี pythonic สำหรับค่าคงที่คือการไม่เติบโตชั้นเรียนสำหรับค่าคงที่ เพียงแค่มีบางคนconst.pyที่มีPI = 3.14และคุณสามารถนำมันทุกที่ from const import PI
Giszmo

30
คำตอบนี้น่าจะสร้างความสับสนให้กับปัญหาตัวแปรแบบคงที่ เพื่อเริ่มต้นกับi = 3จะไม่ตัวแปรคงมันเป็นแอตทริบิวต์ชั้นเรียนและเพราะมันเป็นความแตกต่างจากแอตทริบิวต์เช่นระดับiมันไม่ได้ทำตัวเหมือนตัวแปรคงที่ในภาษาอื่น ๆ ดูคำตอบของ millerdev , คำตอบ Yann ของและคำตอบของฉันด้านล่าง
Rick สนับสนุนโมนิก้า

2
ดังนั้นเพียงหนึ่งสำเนาi(ตัวแปรคงที่) จะอยู่ในหน่วยความจำแม้ว่าฉันจะสร้างอินสแตนซ์ของคลาสนี้เป็นร้อยหรือไม่
sdream

2
สำหรับใครสนใจใครที่แดเนียลพูดถึงในความคิดเห็น @Dubslow มันเป็นmillerdev ( เครื่อง wayback )
HeyJude

618

@Blair Conrad กล่าวว่าตัวแปรสแตติกที่ประกาศในคำจำกัดความของคลาส แต่ไม่ใช่ภายในเมธอดคือตัวแปร class หรือ "static":

>>> class Test(object):
...     i = 3
...
>>> Test.i
3

มี gotcha อยู่สองสามที่นี่ ดำเนินการจากตัวอย่างด้านบน:

>>> t = Test()
>>> t.i     # "static" variable accessed via instance
3
>>> t.i = 5 # but if we assign to the instance ...
>>> Test.i  # we have not changed the "static" variable
3
>>> t.i     # we have overwritten Test.i on t by creating a new attribute t.i
5
>>> Test.i = 6 # to change the "static" variable we do it by assigning to the class
>>> t.i
5
>>> Test.i
6
>>> u = Test()
>>> u.i
6           # changes to t do not affect new instances of Test

# Namespaces are one honking great idea -- let's do more of those!
>>> Test.__dict__
{'i': 6, ...}
>>> t.__dict__
{'i': 5}
>>> u.__dict__
{}

แจ้งให้ทราบว่าตัวแปรเช่นt.iได้ออกจากซิงค์กับ "คงที่" ตัวแปรระดับเมื่อแอตทริบิวต์ถูกตั้งโดยตรงบนi tนี่เป็นเพราะiถูกผูกไว้ใหม่ภายในtเนมสเปซซึ่งแตกต่างจากTestเนมสเปซ หากคุณต้องการเปลี่ยนค่าของตัวแปร "สแตติก" คุณต้องเปลี่ยนภายในขอบเขต (หรือวัตถุ) ที่ถูกกำหนดไว้ตั้งแต่แรก ฉันใส่ "static" ในเครื่องหมายคำพูดเพราะ Python ไม่มีตัวแปรสแตติกในแง่ที่ C ++ และ Java ทำ

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

@Steve Johnson ยังได้รับคำตอบเกี่ยวกับวิธีการคงที่ซึ่งมีการบันทึกไว้ใน "ฟังก์ชั่นในตัว" ใน Python Library Reference

class Test(object):
    @staticmethod
    def f(arg1, arg2, ...):
        ...

@beid ยังกล่าวถึง classmethod ซึ่งคล้ายกับ staticmethod อาร์กิวเมนต์แรกของ classmethod คืออ็อบเจ็กต์คลาส ตัวอย่าง:

class Test(object):
    i = 3 # class (or static) variable
    @classmethod
    def g(cls, arg):
        # here we can use 'cls' instead of the class name (Test)
        if arg > cls.i:
            cls.i = arg # would be the same as Test.i = arg1

การเป็นตัวแทนภาพด้านบนตัวอย่าง


3
ฉันขอแนะนำให้คุณขยายตัวอย่างเพียงเล็กน้อย: ถ้าหลังจากตั้งค่า Test.i = 6 คุณจะยกตัวอย่างวัตถุใหม่ (เช่น u = Test ()) วัตถุใหม่จะ "สืบทอด" ค่าคลาสใหม่ (เช่น ui == 6)
ทำเครื่องหมาย

2
วิธีที่จะทำให้ตัวแปรคงที่ในการซิงค์คือการทำให้พวกเขาคุณสมบัติ: class Test(object):, _i = 3, @property, def i(self), return type(self)._i, @i.setter, ,def i(self,val): type(self)._i = valตอนนี้คุณสามารถทำx = Test(), ,x.i = 12 assert x.i == Test.i
Rick สนับสนุนโมนิกา

1
ดังนั้นฉันสามารถพูดได้ว่าตัวแปรทั้งหมดเป็นแบบคงที่ในขั้นต้นแล้วการเข้าถึงอินสแตนซ์ทำให้ตัวแปรอินสแตนซ์ที่รันไทม์?
อาลี

บางทีนี่อาจเป็นเรื่องที่น่าสนใจ: หากคุณกำหนดวิธีในการทดสอบที่เปลี่ยน Test.i ซึ่งจะมีผลต่อค่า BOTH Test.i และ ti
Pablo

@millerdev เช่นที่คุณพูดถึง Python ไม่มีตัวแปรสแตติกเนื่องจาก C ++ หรือ JAVA มี .. ดังนั้นจะเป็นการดีหรือไม่ที่จะบอกว่า Test.i เป็นตัวแปรคลาสมากกว่าตัวแปรสแตติกหรือไม่
Tyto

197

วิธีการคงที่และระดับ

ดังที่คำตอบอื่น ๆ ได้บันทึกไว้วิธีการคงที่และวิธีการเรียนสามารถทำได้อย่างง่ายดายโดยใช้ตัวตกแต่งภายใน:

class Test(object):

    # regular instance method:
    def MyMethod(self):
        pass

    # class method:
    @classmethod
    def MyClassMethod(klass):
        pass

    # static method:
    @staticmethod
    def MyStaticMethod():
        pass

ตามปกติอาร์กิวเมนต์แรกที่MyMethod()ถูกผูกไว้กับวัตถุอินสแตนซ์ของคลาส ในทางตรงกันข้ามอาร์กิวเมนต์แรกที่MyClassMethod()ถูกผูกไว้กับวัตถุคลาสตัวเอง (เช่นในกรณีนี้Test) สำหรับMyStaticMethod()ไม่มีข้อโต้แย้งที่ถูกผูกไว้และมีข้อโต้แย้งที่ทุกคนเป็นตัวเลือก

"ตัวแปรคงที่"

อย่างไรก็ตามการใช้ "ตัวแปรแบบคงที่" (เช่นกันตัวแปรแบบคงที่ที่ผันแปรไม่ได้ถ้าไม่ใช่ความขัดแย้งในแง่ ... ) จะไม่ตรงไปข้างหน้า ดังที่ millerdev ชี้ให้เห็นในคำตอบของเขาปัญหาคือคุณลักษณะระดับ Python ไม่ใช่ "ตัวแปรคงที่" อย่างแท้จริง พิจารณา:

class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i

นี่เป็นเพราะบรรทัดx.i = 12ได้เพิ่มแอททริบิวต์อินสแตนซ์ใหม่iเป็นxแทนที่จะเปลี่ยนค่าของแอททริบิวต์Testคลาสi

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

class Test(object):

    _i = 3

    @property
    def i(self):
        return type(self)._i

    @i.setter
    def i(self,val):
        type(self)._i = val

## ALTERNATIVE IMPLEMENTATION - FUNCTIONALLY EQUIVALENT TO ABOVE ##
## (except with separate methods for getting and setting i) ##

class Test(object):

    _i = 3

    def get_i(self):
        return type(self)._i

    def set_i(self,val):
        type(self)._i = val

    i = property(get_i, set_i)

ตอนนี้คุณสามารถทำได้:

x1 = Test()
x2 = Test()
x1.i = 50
assert x2.i == x1.i  # no error
assert x2.i == 50    # the property is synced

ตัวแปรคงที่ในขณะนี้จะยังคงอยู่ในการซิงค์ระหว่างอินสแตนซ์ระดับทั้งหมด

(หมายเหตุ: นั่นคือเว้นแต่ว่าคลาสอินสแตนซ์ตัดสินใจที่จะกำหนดเวอร์ชันของตัวเอง_i! แต่ถ้ามีคนตัดสินใจที่จะทำเช่นนั้นพวกเขาสมควรได้รับสิ่งที่พวกเขาได้รับพวกเขาไม่?

โปรดทราบว่าการพูดทางเทคนิคiยังคงไม่ใช่ 'ตัวแปรคงที่' ทั้งหมด มันคือ a property, ซึ่งเป็น descriptor ชนิดพิเศษ อย่างไรก็ตามpropertyพฤติกรรมนี้เทียบเท่ากับตัวแปรแบบคงที่ (ไม่แน่นอน) ที่ซิงค์ในทุกอินสแตนซ์ของคลาส

"ตัวแปรคงที่" ไม่เปลี่ยนรูป

สำหรับการทำงานของตัวแปรสแตติกที่ไม่เปลี่ยนรูปแบบเพียงแค่ละเว้นpropertysetter:

class Test(object):

    _i = 3

    @property
    def i(self):
        return type(self)._i

## ALTERNATIVE IMPLEMENTATION - FUNCTIONALLY EQUIVALENT TO ABOVE ##
## (except with separate methods for getting i) ##

class Test(object):

    _i = 3

    def get_i(self):
        return type(self)._i

    i = property(get_i)

ตอนนี้ความพยายามที่จะตั้งค่าiแอตทริบิวต์ของอินสแตนซ์จะคืนค่าAttributeError:

x = Test()
assert x.i == 3  # success
x.i = 12         # ERROR

หนึ่ง Gotcha ที่ต้องระวัง

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

x = Test()
assert x.i == Test.i  # ERROR

# x.i and Test.i are two different objects:
type(Test.i)  # class 'property'
type(x.i)     # class 'int'

บรรทัดassert Test.i == x.iสร้างข้อผิดพลาดเนื่องจากiแอตทริบิวต์ของTestและxเป็นวัตถุสองชนิดที่แตกต่างกัน

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

    i = property(get_i) 

เห็นได้ชัดว่าสมาชิกiของTestจะต้องเป็นpropertyวัตถุซึ่งเป็นประเภทของวัตถุที่ส่งคืนจากpropertyฟังก์ชั่น

หากคุณพบว่ามีความสับสนข้างต้นคุณมีแนวโน้มที่จะยังคงคิดถึงมันจากมุมมองของภาษาอื่น ๆ (เช่น Java หรือ c ++) คุณควรไปศึกษาpropertyวัตถุเกี่ยวกับลำดับที่ส่งคืนแอ็ตทริบิวต์ Python, โปรโตคอล descriptor และลำดับการแก้ไขเมธอด (MRO)

ฉันนำเสนอวิธีแก้ปัญหาข้างต้น 'gotcha' ด้านล่าง; อย่างไรก็ตามฉันขอแนะนำ - อย่างแรง - ว่าคุณไม่ได้พยายามทำสิ่งต่อไปนี้จนกระทั่ง - อย่างน้อยคุณเข้าใจอย่างถ่องแท้ว่าทำไมจึงassert Test.i = x.iเกิดข้อผิดพลาด

ตัวแปรคงที่จริง, ที่เกิดขึ้นจริง -Test.i == x.i

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

UPDATE: ความพยายามนี้เป็นจริงอันยิ่งใหญ่สวย ; หากคุณยืนยันที่จะทำอะไรเช่นนี้ (คำใบ้: โปรดอย่า Python เป็นภาษาที่หรูหรามากและใช้รองเท้าในการทำตัวเหมือนภาษาอื่นที่ไม่จำเป็น) ให้ใช้รหัสในคำตอบของ Ethan Furmanแทน

การจำลองพฤติกรรมคงที่ตัวแปรของภาษาอื่น ๆ โดยใช้ metaclass

เมตาคลาสเป็นคลาสของคลาส metaclass เริ่มต้นสำหรับชั้นเรียนทั้งหมดในหลาม (กล่าวคือ "รูปแบบใหม่" เรียนโพสต์หลาม 2.3 ผมเชื่อว่า) typeเป็น ตัวอย่างเช่น:

type(int)  # class 'type'
type(str)  # class 'type'
class Test(): pass
type(Test) # class 'type'

อย่างไรก็ตามคุณสามารถกำหนด metaclass ของคุณเองดังนี้:

class MyMeta(type): pass

และนำไปใช้กับคลาสของคุณเช่นนี้ (Python 3 เท่านั้น):

class MyClass(metaclass = MyMeta):
    pass

type(MyClass)  # class MyMeta

ด้านล่างเป็น metaclass ที่ฉันสร้างขึ้นซึ่งพยายามเลียนแบบพฤติกรรม "ตัวแปรคงที่" ของภาษาอื่น มันทำงานได้โดยการแทนที่ getter, setter และ deleter ที่เป็นค่าเริ่มต้นด้วยเวอร์ชันที่ตรวจสอบเพื่อดูว่าแอตทริบิวต์ที่ร้องขอนั้นเป็น "ตัวแปรคงที่" หรือไม่

แคตตาล็อกของ "ตัวแปรคงที่" จะถูกเก็บไว้ในStaticVarMeta.staticsแอตทริบิวต์ การร้องขอคุณลักษณะทั้งหมดจะพยายามแก้ไขโดยใช้ลำดับการแก้ไขแทน ฉันได้ขนานนามว่า "ลำดับความละเอียดคงที่" หรือ "SRO" สิ่งนี้ทำได้โดยค้นหาแอตทริบิวต์ที่ร้องขอในชุดของ "ตัวแปรคงที่" สำหรับคลาสที่กำหนด (หรือคลาสแม่ของมัน) หากแอตทริบิวต์ไม่ปรากฏใน "SRO" คลาสจะถอยกลับไปที่แอตทริบิวต์เริ่มต้นคือรับ / set / delete พฤติกรรม (เช่น "MRO")

from functools import wraps

class StaticVarsMeta(type):
    '''A metaclass for creating classes that emulate the "static variable" behavior
    of other languages. I do not advise actually using this for anything!!!

    Behavior is intended to be similar to classes that use __slots__. However, "normal"
    attributes and __statics___ can coexist (unlike with __slots__). 

    Example usage: 

        class MyBaseClass(metaclass = StaticVarsMeta):
            __statics__ = {'a','b','c'}
            i = 0  # regular attribute
            a = 1  # static var defined (optional)

        class MyParentClass(MyBaseClass):
            __statics__ = {'d','e','f'}
            j = 2              # regular attribute
            d, e, f = 3, 4, 5  # Static vars
            a, b, c = 6, 7, 8  # Static vars (inherited from MyBaseClass, defined/re-defined here)

        class MyChildClass(MyParentClass):
            __statics__ = {'a','b','c'}
            j = 2  # regular attribute (redefines j from MyParentClass)
            d, e, f = 9, 10, 11   # Static vars (inherited from MyParentClass, redefined here)
            a, b, c = 12, 13, 14  # Static vars (overriding previous definition in MyParentClass here)'''
    statics = {}
    def __new__(mcls, name, bases, namespace):
        # Get the class object
        cls = super().__new__(mcls, name, bases, namespace)
        # Establish the "statics resolution order"
        cls.__sro__ = tuple(c for c in cls.__mro__ if isinstance(c,mcls))

        # Replace class getter, setter, and deleter for instance attributes
        cls.__getattribute__ = StaticVarsMeta.__inst_getattribute__(cls, cls.__getattribute__)
        cls.__setattr__ = StaticVarsMeta.__inst_setattr__(cls, cls.__setattr__)
        cls.__delattr__ = StaticVarsMeta.__inst_delattr__(cls, cls.__delattr__)
        # Store the list of static variables for the class object
        # This list is permanent and cannot be changed, similar to __slots__
        try:
            mcls.statics[cls] = getattr(cls,'__statics__')
        except AttributeError:
            mcls.statics[cls] = namespace['__statics__'] = set() # No static vars provided
        # Check and make sure the statics var names are strings
        if any(not isinstance(static,str) for static in mcls.statics[cls]):
            typ = dict(zip((not isinstance(static,str) for static in mcls.statics[cls]), map(type,mcls.statics[cls])))[True].__name__
            raise TypeError('__statics__ items must be strings, not {0}'.format(typ))
        # Move any previously existing, not overridden statics to the static var parent class(es)
        if len(cls.__sro__) > 1:
            for attr,value in namespace.items():
                if attr not in StaticVarsMeta.statics[cls] and attr != ['__statics__']:
                    for c in cls.__sro__[1:]:
                        if attr in StaticVarsMeta.statics[c]:
                            setattr(c,attr,value)
                            delattr(cls,attr)
        return cls
    def __inst_getattribute__(self, orig_getattribute):
        '''Replaces the class __getattribute__'''
        @wraps(orig_getattribute)
        def wrapper(self, attr):
            if StaticVarsMeta.is_static(type(self),attr):
                return StaticVarsMeta.__getstatic__(type(self),attr)
            else:
                return orig_getattribute(self, attr)
        return wrapper
    def __inst_setattr__(self, orig_setattribute):
        '''Replaces the class __setattr__'''
        @wraps(orig_setattribute)
        def wrapper(self, attr, value):
            if StaticVarsMeta.is_static(type(self),attr):
                StaticVarsMeta.__setstatic__(type(self),attr, value)
            else:
                orig_setattribute(self, attr, value)
        return wrapper
    def __inst_delattr__(self, orig_delattribute):
        '''Replaces the class __delattr__'''
        @wraps(orig_delattribute)
        def wrapper(self, attr):
            if StaticVarsMeta.is_static(type(self),attr):
                StaticVarsMeta.__delstatic__(type(self),attr)
            else:
                orig_delattribute(self, attr)
        return wrapper
    def __getstatic__(cls,attr):
        '''Static variable getter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                try:
                    return getattr(c,attr)
                except AttributeError:
                    pass
        raise AttributeError(cls.__name__ + " object has no attribute '{0}'".format(attr))
    def __setstatic__(cls,attr,value):
        '''Static variable setter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                setattr(c,attr,value)
                break
    def __delstatic__(cls,attr):
        '''Static variable deleter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                try:
                    delattr(c,attr)
                    break
                except AttributeError:
                    pass
        raise AttributeError(cls.__name__ + " object has no attribute '{0}'".format(attr))
    def __delattr__(cls,attr):
        '''Prevent __sro__ attribute from deletion'''
        if attr == '__sro__':
            raise AttributeError('readonly attribute')
        super().__delattr__(attr)
    def is_static(cls,attr):
        '''Returns True if an attribute is a static variable of any class in the __sro__'''
        if any(attr in StaticVarsMeta.statics[c] for c in cls.__sro__):
            return True
        return False

ฉันพยายามใช้วิธีการของคุณ แต่ฉันประสบปัญหากรุณาดูคำถามของฉันที่นี่stackoverflow.com/questions/29329850/get-static-variable-value
Muhammed Refaat

@RickTeachey: ฉันคิดว่าคุณควรจะดูทุกอย่างที่คุณทำในอินสแตนซ์ของคลาสTest(ก่อนที่จะใช้เพื่ออินสแตนซ์อินสแตนซ์ของอินสแตนซ์) ในโดเมนของเมตาโปรแกรม ตัวอย่างเช่นคุณเปลี่ยนพฤติกรรมคลาสโดยทำTest.i = 0(ที่นี่คุณเพียงทำลายวัตถุทรัพย์สินทั้งหมด) ฉันเดาว่า "คุณสมบัติกลไก" เตะเฉพาะในการเข้าถึงคุณสมบัติในอินสแตนซ์ของคลาส (เว้นแต่ว่าคุณจะเปลี่ยนพฤติกรรมพื้นฐานโดยใช้เมตาคลาสเป็นระดับกลางบางที) Btw โปรดตอบคำถามนี้ให้เสร็จ :-)
Ole Thomsen Buus

1
@RickTeachey ขอบคุณ :-) metaclass ของคุณในตอนท้ายน่าสนใจ แต่จริงๆแล้วมันซับซ้อนเกินไปสำหรับความชอบของฉัน มันอาจมีประโยชน์ในกรอบ / แอพพลิเคชั่นขนาดใหญ่ที่จำเป็นต้องใช้กลไกนี้ อย่างไรก็ตามโรมรันนี้ว่าถ้าใหม่ (ซับซ้อน) เมตาพฤติกรรมไม่ใช่ค่าเริ่มต้นมีความจำเป็นจริงๆ, Python ทำให้มันเป็นไปได้ :)
Ole Thomsen Buus

1
@OleThomsenBuus: ตรวจสอบคำตอบของฉันสำหรับ metaclass ที่เรียบง่ายกว่าซึ่งทำงานได้
Ethan Furman

1
@taper คุณถูกต้อง; ฉันได้แก้ไขคำตอบเพื่อแก้ไขปัญหา (ไม่อยากจะเชื่อว่ามันนั่งอยู่ที่นั่นผิดมานานแล้ว!) ขอโทษสำหรับความสับสน.
Rick สนับสนุนโมนิก้า

33

คุณยังสามารถเพิ่มตัวแปรคลาสให้กับคลาสได้ทันที

>>> class X:
...     pass
... 
>>> X.bar = 0
>>> x = X()
>>> x.bar
0
>>> x.foo
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
AttributeError: X instance has no attribute 'foo'
>>> X.foo = 1
>>> x.foo
1

และอินสแตนซ์ของคลาสสามารถเปลี่ยนตัวแปรคลาสได้

class X:
  l = []
  def __init__(self):
    self.l.append(1)

print X().l
print X().l

>python test.py
[1]
[1, 1]

3
ตัวแปรคลาสใหม่จะติดแม้ว่าคลาสจะถูกอิมพอร์ตไปยังโมดูลอื่นหรือไม่?
zakdances

ใช่. คลาสเป็น singletons ที่มีประสิทธิภาพโดยไม่คำนึงถึงเนมสเปซที่คุณโทรหา
Pedro

@ Gregory คุณพูดว่า "และอินสแตนซ์ของคลาสสามารถเปลี่ยนตัวแปรคลาสได้" จริงๆแล้วตัวอย่างนี้เรียกว่าการเข้าถึงไม่ได้แก้ไข การปรับเปลี่ยนทำได้โดยวัตถุเองผ่านฟังก์ชั่นผนวก () ของตัวเอง
Amr ALHOSSARY

19

ส่วนตัวฉันจะใช้ classmethod ทุกครั้งที่ฉันต้องการวิธีการคงที่ ส่วนใหญ่เป็นเพราะฉันได้รับชั้นเรียนเป็นข้อโต้แย้ง

class myObj(object):
   def myMethod(cls)
     ...
   myMethod = classmethod(myMethod) 

หรือใช้มัณฑนากร

class myObj(object):
   @classmethod
   def myMethod(cls)

สำหรับคุณสมบัติสแตติก .. ถึงเวลาที่คุณต้องค้นหานิยามของไพ ธ อน .. ตัวแปรสามารถเปลี่ยนแปลงได้ มีสองประเภทที่ไม่แน่นอนและไม่เปลี่ยนรูปได้นอกจากนี้ยังมีแอตทริบิวต์คลาสและแอตทริบิวต์ของแอตทริบิวต์ ... ไม่มีอะไรที่เหมือนกับแอตทริบิวต์แบบคงที่ในความรู้สึกของ java & c ++

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


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

Java และ C ++ ใช้สแตติก (ใช้คำที่ไม่ถูกต้อง, imho) เหมือนกับที่คุณใช้อินสแตนซ์กับแอตทริบิวต์ class แอตทริบิวต์ / เมธอดคลาสนั้นเป็นแบบสแตติกใน Java และ C ++, ไม่ต่างกันยกเว้นว่าใน Python พารามิเตอร์แรกของการเรียกเมธอดคลาสคือคลาส
Angel O'Sphere

16

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

class my_cls:
  my_prop = 0

#static property
print my_cls.my_prop  #--> 0

#assign value to static property
my_cls.my_prop = 1 
print my_cls.my_prop  #--> 1

#access static property thru' instance
my_inst = my_cls()
print my_inst.my_prop #--> 1

#instance property is different from static property 
#after being assigned a value
my_inst.my_prop = 2
print my_cls.my_prop  #--> 1
print my_inst.my_prop #--> 2

ซึ่งหมายความว่าก่อนที่จะกำหนดค่าให้กับคุณสมบัติของอินสแตนซ์ถ้าเราพยายามเข้าถึงอินสแตนซ์ของคุณสมบัติผ่าน 'จะใช้ค่าคงที่ สถานที่ให้บริการที่ประกาศในระดับหลามแต่ละมักจะมีช่องเสียบแบบคงที่ในหน่วยความจำ


16

วิธีการแบบคงที่ในงูใหญ่เรียกว่าclassmethod s ลองดูรหัสต่อไปนี้

class MyClass:

    def myInstanceMethod(self):
        print 'output from an instance method'

    @classmethod
    def myStaticMethod(cls):
        print 'output from a static method'

>>> MyClass.myInstanceMethod()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method myInstanceMethod() must be called [...]

>>> MyClass.myStaticMethod()
output from a static method

โปรดสังเกตว่าเมื่อเราเรียกใช้เมธอดmyInstanceMethodเราจะได้รับข้อผิดพลาด นี่เป็นเพราะมันต้องการวิธีการนั้นถูกเรียกบนอินสแตนซ์ของคลาสนี้ วิธีการmyStaticMethodถูกตั้งค่าเป็น classmethod โดยใช้มัณฑนากร @classmethod

เพียงแค่เตะและหัวเราะคิกคักเราสามารถเรียกmyInstanceวิธีการในชั้นเรียนโดยผ่านในตัวอย่างของชั้นเรียนเช่น:

>>> MyClass.myInstanceMethod(MyClass())
output from an instance method

2
อืมม ... วิธีการคงที่ทำด้วย@staticmethod; @classmethodคือ (ชัด) สำหรับวิธีการเรียน (ซึ่งมีวัตถุประสงค์หลักเพื่อใช้เป็นตัวสร้างทางเลือก แต่สามารถให้บริการในหยิกเป็นวิธีการคงที่ซึ่งเกิดขึ้นที่จะได้รับการอ้างอิงถึงชั้นเรียนที่พวกเขาถูกเรียกผ่าน)
ShadowRanger

11

เมื่อกำหนดตัวแปรสมาชิกบางตัวนอกเมธอดสมาชิกใด ๆ ตัวแปรสามารถเป็นแบบคงที่หรือไม่คงที่ขึ้นอยู่กับวิธีการแสดงตัวแปร

  • CLASSNAME.var เป็นตัวแปรคงที่
  • INSTANCENAME.var ไม่ใช่ตัวแปรคงที่
  • self.var ภายในชั้นเรียนไม่ได้เป็นตัวแปรคงที่
  • var ภายในฟังก์ชันสมาชิกคลาสไม่ได้ถูกกำหนดไว้

ตัวอย่างเช่น:

#!/usr/bin/python

class A:
    var=1

    def printvar(self):
        print "self.var is %d" % self.var
        print "A.var is %d" % A.var


    a = A()
    a.var = 2
    a.printvar()

    A.var = 3
    a.printvar()

ผลที่ได้คือ

self.var is 2
A.var is 1
self.var is 2
A.var is 3

การเยื้องหัก สิ่งนี้จะไม่ดำเนินการ
Thomas Weller

9

เป็นไปได้ที่จะมีstaticตัวแปรคลาส แต่อาจไม่คุ้มค่ากับความพยายาม

ต่อไปนี้เป็นข้อพิสูจน์แนวคิดที่เขียนใน Python 3 หากรายละเอียดที่ถูกต้องผิดรหัสสามารถปรับแต่งให้ตรงกับสิ่งที่คุณหมายถึงโดยstatic variable:


class Static:
    def __init__(self, value, doc=None):
        self.deleted = False
        self.value = value
        self.__doc__ = doc
    def __get__(self, inst, cls=None):
        if self.deleted:
            raise AttributeError('Attribute not set')
        return self.value
    def __set__(self, inst, value):
        self.deleted = False
        self.value = value
    def __delete__(self, inst):
        self.deleted = True

class StaticType(type):
    def __delattr__(cls, name):
        obj = cls.__dict__.get(name)
        if isinstance(obj, Static):
            obj.__delete__(name)
        else:
            super(StaticType, cls).__delattr__(name)
    def __getattribute__(cls, *args):
        obj = super(StaticType, cls).__getattribute__(*args)
        if isinstance(obj, Static):
            obj = obj.__get__(cls, cls.__class__)
        return obj
    def __setattr__(cls, name, val):
        # check if object already exists
        obj = cls.__dict__.get(name)
        if isinstance(obj, Static):
            obj.__set__(name, val)
        else:
            super(StaticType, cls).__setattr__(name, val)

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

class MyStatic(metaclass=StaticType):
    """
    Testing static vars
    """
    a = Static(9)
    b = Static(12)
    c = 3

class YourStatic(MyStatic):
    d = Static('woo hoo')
    e = Static('doo wop')

และการทดสอบบางอย่าง:

ms1 = MyStatic()
ms2 = MyStatic()
ms3 = MyStatic()
assert ms1.a == ms2.a == ms3.a == MyStatic.a
assert ms1.b == ms2.b == ms3.b == MyStatic.b
assert ms1.c == ms2.c == ms3.c == MyStatic.c
ms1.a = 77
assert ms1.a == ms2.a == ms3.a == MyStatic.a
ms2.b = 99
assert ms1.b == ms2.b == ms3.b == MyStatic.b
MyStatic.a = 101
assert ms1.a == ms2.a == ms3.a == MyStatic.a
MyStatic.b = 139
assert ms1.b == ms2.b == ms3.b == MyStatic.b
del MyStatic.b
for inst in (ms1, ms2, ms3):
    try:
        getattr(inst, 'b')
    except AttributeError:
        pass
    else:
        print('AttributeError not raised on %r' % attr)
ms1.c = 13
ms2.c = 17
ms3.c = 19
assert ms1.c == 13
assert ms2.c == 17
assert ms3.c == 19
MyStatic.c = 43
assert ms1.c == 13
assert ms2.c == 17
assert ms3.c == 19

ys1 = YourStatic()
ys2 = YourStatic()
ys3 = YourStatic()
MyStatic.b = 'burgler'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a
assert ys1.b == ys2.b == ys3.b == YourStatic.b == MyStatic.b
assert ys1.d == ys2.d == ys3.d == YourStatic.d
assert ys1.e == ys2.e == ys3.e == YourStatic.e
ys1.a = 'blah'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a
ys2.b = 'kelp'
assert ys1.b == ys2.b == ys3.b == YourStatic.b == MyStatic.b
ys1.d = 'fee'
assert ys1.d == ys2.d == ys3.d == YourStatic.d
ys2.e = 'fie'
assert ys1.e == ys2.e == ys3.e == YourStatic.e
MyStatic.a = 'aargh'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a

8

คุณสามารถบังคับคลาสให้เป็นแบบสแตติกโดยใช้ metaclass

class StaticClassError(Exception):
    pass


class StaticClass:
    __metaclass__ = abc.ABCMeta

    def __new__(cls, *args, **kw):
        raise StaticClassError("%s is a static class and cannot be initiated."
                                % cls)

class MyClass(StaticClass):
    a = 1
    b = 3

    @staticmethod
    def add(x, y):
        return x+y

จากนั้นเมื่อใดก็ตามที่คุณพยายามเริ่มต้นMyClassคุณจะได้รับ StaticClassError


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

1
แอนเดอสำนวนเป็นวิธีที่ดีในการจัดการนี้
Rick สนับสนุนโมนิก้า

@NedBatchelder มันเป็นระดับนามธรรมมีไว้สำหรับ subclassing เท่านั้น (และอินสแตนซ์ของคลาสย่อย)
stevepastelan

1
ฉันหวังว่า subclasses จะไม่ใช้ super () เพื่อเรียกใช้__new__พ่อแม่ของมัน ...
เน็ดแบทเชลเดอร์

7

จุดหนึ่งที่น่าสนใจมากเกี่ยวกับการค้นหาคุณลักษณะของ Python คือสามารถใช้เพื่อสร้าง " ตัวแปรเสมือน ":

class A(object):

  label="Amazing"

  def __init__(self,d): 
      self.data=d

  def say(self): 
      print("%s %s!"%(self.label,self.data))

class B(A):
  label="Bold"  # overrides A.label

A(5).say()      # Amazing 5!
B(3).say()      # Bold 3!

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


6

ในเรื่องที่เกี่ยวกับเรื่องนี้คำตอบสำหรับค่าคงที่ตัวแปรคงที่คุณสามารถใช้บ่ง นี่คือตัวอย่าง:

class ConstantAttribute(object):
    '''You can initialize my value but not change it.'''
    def __init__(self, value):
        self.value = value

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

    def __set__(self, obj, val):
        pass


class Demo(object):
    x = ConstantAttribute(10)


class SubDemo(Demo):
    x = 10


demo = Demo()
subdemo = SubDemo()
# should not change
demo.x = 100
# should change
subdemo.x = 100
print "small demo", demo.x
print "small subdemo", subdemo.x
print "big demo", Demo.x
print "big subdemo", SubDemo.x

ที่เกิดขึ้นใน ...

small demo 10
small subdemo 100
big demo 10
big subdemo 10

คุณสามารถเพิ่มข้อยกเว้นได้เสมอหากไม่สนใจค่าการตั้งค่า ( passด้านบน) อย่างเงียบ ๆไม่ใช่สิ่งที่คุณทำ หากคุณกำลังมองหา C ++ ตัวแปรคลาสแบบคงที่ของ Java:

class StaticAttribute(object):
    def __init__(self, value):
        self.value = value

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

    def __set__(self, obj, val):
        self.value = val

ดูที่คำตอบนี้และเอกสารอย่างเป็นทางการHOWTOสำหรับข้อมูลเพิ่มเติมเกี่ยวกับคำอธิบาย


2
คุณสามารถใช้@propertyซึ่งก็เหมือนกับการใช้ descriptor แต่มันเป็นโค้ดที่น้อยกว่ามาก
Rick สนับสนุนโมนิก้า

6

ใช่แล้ว Python เองไม่มีสมาชิกข้อมูลคงที่อย่างชัดเจน แต่เราสามารถทำได้โดยทำเช่นนั้น

class A:
    counter =0
    def callme (self):
        A.counter +=1
    def getcount (self):
        return self.counter  
>>> x=A()
>>> y=A()
>>> print(x.getcount())
>>> print(y.getcount())
>>> x.callme() 
>>> print(x.getcount())
>>> print(y.getcount())

เอาท์พุต

0
0
1
1

คำอธิบาย

here object (x) alone increment the counter variable
from 0 to 1 by not object y. But result it as "static counter"

6

ใช่เป็นไปได้แน่นอนในการเขียนตัวแปรและวิธีการแบบคงที่ในไพ ธ อน

ตัวแปรแบบคงที่: ตัวแปรที่ประกาศในระดับชั้นจะเรียกว่าตัวแปรแบบคงที่ซึ่งสามารถเข้าถึงได้โดยตรงโดยใช้ชื่อชั้น

    >>> class A:
        ...my_var = "shagun"

    >>> print(A.my_var)
        shagun

ตัวแปรอินสแตนซ์:ตัวแปรที่เกี่ยวข้องและเข้าถึงได้โดยอินสแตนซ์ของคลาสเป็นตัวแปรอินสแตนซ์

   >>> a = A()
   >>> a.my_var = "pruthi"
   >>> print(A.my_var,a.my_var)
       shagun pruthi

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

แต่โปรดจำไว้ว่าวิธีการคงที่ไม่สามารถเรียกวิธีการไม่คงที่ในงูหลาม

    >>> class A:
   ...     @staticmethod
   ...     def my_static_method():
   ...             print("Yippey!!")
   ... 
   >>> A.my_static_method()
   Yippey!!

4

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

วัตถุชนิดดั้งเดิมบางชนิดเช่นจำนวนเต็มลอยสายอักขระและ touples ไม่สามารถเปลี่ยนแปลงได้ใน Python ซึ่งหมายความว่าวัตถุที่อ้างถึงโดยชื่อที่กำหนดไม่สามารถเปลี่ยนแปลงได้หากเป็นวัตถุประเภทใดชนิดหนึ่งดังกล่าวข้างต้น สามารถกำหนดชื่อให้กับวัตถุอื่นได้ แต่วัตถุนั้นอาจไม่สามารถเปลี่ยนแปลงได้

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


4

วิธีที่ดีที่สุดที่ฉันพบคือใช้คลาสอื่น คุณสามารถสร้างวัตถุแล้วใช้มันกับวัตถุอื่น ๆ

class staticFlag:
    def __init__(self):
        self.__success = False
    def isSuccess(self):
        return self.__success
    def succeed(self):
        self.__success = True

class tryIt:
    def __init__(self, staticFlag):
        self.isSuccess = staticFlag.isSuccess
        self.succeed = staticFlag.succeed

tryArr = []
flag = staticFlag()
for i in range(10):
    tryArr.append(tryIt(flag))
    if i == 5:
        tryArr[i].succeed()
    print tryArr[i].isSuccess()

staticFlagจากตัวอย่างข้างต้นผมทำชั้นที่มีชื่อว่า

คลาสนี้ควรแสดง var แบบคงที่__success(Private Static Var)

tryIt class แสดงคลาสปกติที่เราต้องการใช้

ตอนนี้ฉันสร้างวัตถุสำหรับหนึ่งธง ( staticFlag) ธงนี้จะถูกส่งไปเป็นการอ้างอิงถึงวัตถุปกติทั้งหมด

tryArrวัตถุทั้งหมดเหล่านี้จะถูกเพิ่มลงในรายการ


ผลลัพธ์ของสคริปต์นี้:

False
False
False
False
False
True
True
True
True
True

2

ตัวแปรสแตติกในคลาสโรงงาน python3.6

สำหรับใครก็ตามที่ใช้คลาสโรงงานที่มีpython3.6ขึ้นไปให้ใช้nonlocalคีย์เวิร์ดเพื่อเพิ่มเข้าไปในขอบเขต / บริบทของคลาสที่ถูกสร้างเช่น:

>>> def SomeFactory(some_var=None):
...     class SomeClass(object):
...         nonlocal some_var
...         def print():
...             print(some_var)
...     return SomeClass
... 
>>> SomeFactory(some_var="hello world").print()
hello world

ใช่ แต่ในกรณีนี้คือhasattr(SomeClass, 'x') Falseฉันสงสัยว่านี่คือสิ่งที่ทุกคนหมายถึงโดยตัวแปรคงที่ทั้งหมด
Rick สนับสนุนโมนิก้า

@RickTeachey ฮ่า ๆ เห็นรหัสตัวแปรคงที่ของคุณstackoverflow.com/a/27568860/2026508 +1 อินเทอร์เน็ตเซอร์และฉันคิดว่า Hasattr ไม่ทำงานเช่นนั้น? เช่นนั้นsome_varไม่เปลี่ยนรูปและกำหนดแบบคงที่หรือไม่? การเข้าถึงภายนอก getter เกี่ยวข้องกับตัวแปรที่เป็นแบบคงที่หรือไม่? ฉันมีคำถามมากมายตอนนี้ ชอบที่จะได้ยินคำตอบบางอย่างเมื่อคุณมีเวลา
jmunsch

ใช่ว่า metaclass นั้นค่อนข้างไร้สาระ ฉันไม่แน่ใจว่าฉันเข้าใจคำถาม แต่ในใจของฉันsome_varข้างต้นไม่ใช่สมาชิกชั้นเรียนเลย ใน Python สมาชิกคลาสทั้งหมดสามารถเข้าถึงได้จากนอกชั้นเรียน
Rick สนับสนุนโมนิก้า

nonlocalkeywoard "กระแทก" ขอบเขตของตัวแปร ขอบเขตของคำจำกัดความของคลาสร่างกายเป็นอิสระจากขอบเขตที่พบว่าตัวเองภายในเมื่อคุณพูดnonlocal some_varว่าเป็นเพียงการสร้างการอ้างอิงชื่อที่ไม่ใช่ท้องถิ่น (อ่าน: ไม่อยู่ในขอบเขตคำจำกัดความของคลาส) ไปยังวัตถุที่มีชื่ออื่น ดังนั้นจึงไม่ได้แนบมากับคำจำกัดความของคลาสเนื่องจากไม่ได้อยู่ในขอบเขตเนื้อหาของคลาส
Rick สนับสนุนโมนิก้า

1

ดังนั้นนี่อาจเป็นแฮ็ค แต่ฉันเคยใช้eval(str)เพื่อรับวัตถุคงที่ซึ่งเป็นข้อขัดแย้งใน python 3

มีไฟล์ Records.py ที่ไม่มีอะไรนอกจากclassวัตถุที่กำหนดด้วยวิธีการคงที่และตัวสร้างที่บันทึกข้อโต้แย้งบางอย่าง จากนั้นอีกไฟล์. py ฉันimport Recordsแต่ฉันต้องเลือกแต่ละวัตถุแบบไดนามิกแล้วสร้างอินสแตนซ์ตามต้องการตามประเภทของข้อมูลที่กำลังอ่าน

ดังนั้นที่ใดobject_name = 'RecordOne'หรือชื่อคลาสฉันโทรcur_type = eval(object_name)แล้วยกตัวอย่างให้คุณทำcur_inst = cur_type(args) อย่างไรก็ตามก่อนที่คุณจะยกตัวอย่างคุณสามารถเรียกใช้เมธอดสแตติกจากcur_type.getName()ตัวอย่างเช่นการใช้คลาสเบสแบบนามธรรมหรืออะไรก็ตามที่เป็นเป้าหมาย อย่างไรก็ตามในแบ็กเอนด์มันอาจจะสร้างอินสแตนซ์ในไพ ธ อนและไม่คงที่อย่างแท้จริงเพราะอีวาล์กำลังคืนออบเจกต์ .... ซึ่งจะต้องมีอินสแตนซ์ .... ซึ่งให้พฤติกรรมแบบคงที่


0

คุณสามารถใช้รายการหรือพจนานุกรมเพื่อรับ "พฤติกรรมแบบคงที่" ระหว่างอินสแตนซ์

class Fud:

     class_vars = {'origin_open':False}

     def __init__(self, origin = True):
         self.origin = origin
         self.opened = True
         if origin:
             self.class_vars['origin_open'] = True


     def make_another_fud(self):
         ''' Generating another Fud() from the origin instance '''

         return Fud(False)


     def close(self):
         self.opened = False
         if self.origin:
             self.class_vars['origin_open'] = False


fud1 = Fud()
fud2 = fud1.make_another_fud()

print (f"is this the original fud: {fud2.origin}")
print (f"is the original fud open: {fud2.class_vars['origin_open']}")
# is this the original fud: False
# is the original fud open: True

fud1.close()

print (f"is the original fud open: {fud2.class_vars['origin_open']}")
# is the original fud open: False

0

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

# -*- coding: utf-8 -*-
class Worker:
    id = 1

    def __init__(self):
        self.name = ''
        self.document = ''
        self.id = Worker.id
        Worker.id += 1

    def __str__(self):
        return u"{}.- {} {}".format(self.id, self.name, self.document).encode('utf8')


class Workers:
    def __init__(self):
        self.list = []

    def add(self, name, doc):
        worker = Worker()
        worker.name = name
        worker.document = doc
        self.list.append(worker)


if __name__ == "__main__":
    workers = Workers()
    for item in (('Fiona', '0009898'), ('Maria', '66328191'), ("Sandra", '2342184'), ('Elvira', '425872')):
        workers.add(item[0], item[1])
    for worker in workers.list:
        print(worker)
    print("next id: %i" % Worker.id)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.