เป็นไปได้หรือไม่ที่จะมีตัวแปรคลาสหรือสแตติกใน Python? ต้องใช้ไวยากรณ์ใดในการทำเช่นนี้
เป็นไปได้หรือไม่ที่จะมีตัวแปรคลาสหรือสแตติกใน Python? ต้องใช้ไวยากรณ์ใดในการทำเช่นนี้
คำตอบ:
ตัวแปรที่ประกาศภายในนิยามคลาส แต่ไม่ใช่ภายในเมธอดคือตัวแปรคลาสหรือสแตติก:
>>> 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 หากคุณเป็นเช่นนั้นก็คงไม่เป็นไร
const.py
ที่มีPI = 3.14
และคุณสามารถนำมันทุกที่ from const import PI
i = 3
จะไม่ตัวแปรคงมันเป็นแอตทริบิวต์ชั้นเรียนและเพราะมันเป็นความแตกต่างจากแอตทริบิวต์เช่นระดับi
มันไม่ได้ทำตัวเหมือนตัวแปรคงที่ในภาษาอื่น ๆ ดูคำตอบของ millerdev , คำตอบ Yann ของและคำตอบของฉันด้านล่าง
i
(ตัวแปรคงที่) จะอยู่ในหน่วยความจำแม้ว่าฉันจะสร้างอินสแตนซ์ของคลาสนี้เป็นร้อยหรือไม่
@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
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
ดังที่คำตอบอื่น ๆ ได้บันทึกไว้วิธีการคงที่และวิธีการเรียนสามารถทำได้อย่างง่ายดายโดยใช้ตัวตกแต่งภายใน:
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
พฤติกรรมนี้เทียบเท่ากับตัวแปรแบบคงที่ (ไม่แน่นอน) ที่ซิงค์ในทุกอินสแตนซ์ของคลาส
สำหรับการทำงานของตัวแปรสแตติกที่ไม่เปลี่ยนรูปแบบเพียงแค่ละเว้นproperty
setter:
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
โปรดทราบว่าวิธีการดังกล่าวทำงานเฉพาะกับกรณีของชั้นเรียนของคุณ - พวกเขาจะไม่ทำงานเมื่อใช้ชั้นเอง ตัวอย่างเช่น:
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
Test
(ก่อนที่จะใช้เพื่ออินสแตนซ์อินสแตนซ์ของอินสแตนซ์) ในโดเมนของเมตาโปรแกรม ตัวอย่างเช่นคุณเปลี่ยนพฤติกรรมคลาสโดยทำTest.i = 0
(ที่นี่คุณเพียงทำลายวัตถุทรัพย์สินทั้งหมด) ฉันเดาว่า "คุณสมบัติกลไก" เตะเฉพาะในการเข้าถึงคุณสมบัติในอินสแตนซ์ของคลาส (เว้นแต่ว่าคุณจะเปลี่ยนพฤติกรรมพื้นฐานโดยใช้เมตาคลาสเป็นระดับกลางบางที) Btw โปรดตอบคำถามนี้ให้เสร็จ :-)
คุณยังสามารถเพิ่มตัวแปรคลาสให้กับคลาสได้ทันที
>>> 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]
ส่วนตัวฉันจะใช้ classmethod ทุกครั้งที่ฉันต้องการวิธีการคงที่ ส่วนใหญ่เป็นเพราะฉันได้รับชั้นเรียนเป็นข้อโต้แย้ง
class myObj(object):
def myMethod(cls)
...
myMethod = classmethod(myMethod)
หรือใช้มัณฑนากร
class myObj(object):
@classmethod
def myMethod(cls)
สำหรับคุณสมบัติสแตติก .. ถึงเวลาที่คุณต้องค้นหานิยามของไพ ธ อน .. ตัวแปรสามารถเปลี่ยนแปลงได้ มีสองประเภทที่ไม่แน่นอนและไม่เปลี่ยนรูปได้นอกจากนี้ยังมีแอตทริบิวต์คลาสและแอตทริบิวต์ของแอตทริบิวต์ ... ไม่มีอะไรที่เหมือนกับแอตทริบิวต์แบบคงที่ในความรู้สึกของ java & c ++
ทำไมต้องใช้วิธีการแบบคงที่ในความรู้สึกแบบ pythonic ถ้าไม่มีความสัมพันธ์อะไรกับชั้น ถ้าฉันเป็นคุณฉันจะใช้ classmethod หรือกำหนดวิธีการที่เป็นอิสระจากชั้นเรียน
สิ่งหนึ่งที่ควรทราบเกี่ยวกับคุณสมบัติคงที่และคุณสมบัติของอินสแตนซ์แสดงในตัวอย่างด้านล่าง:
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
ซึ่งหมายความว่าก่อนที่จะกำหนดค่าให้กับคุณสมบัติของอินสแตนซ์ถ้าเราพยายามเข้าถึงอินสแตนซ์ของคุณสมบัติผ่าน 'จะใช้ค่าคงที่ สถานที่ให้บริการที่ประกาศในระดับหลามแต่ละมักจะมีช่องเสียบแบบคงที่ในหน่วยความจำ
วิธีการแบบคงที่ในงูใหญ่เรียกว่า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
@staticmethod
; @classmethod
คือ (ชัด) สำหรับวิธีการเรียน (ซึ่งมีวัตถุประสงค์หลักเพื่อใช้เป็นตัวสร้างทางเลือก แต่สามารถให้บริการในหยิกเป็นวิธีการคงที่ซึ่งเกิดขึ้นที่จะได้รับการอ้างอิงถึงชั้นเรียนที่พวกเขาถูกเรียกผ่าน)
เมื่อกำหนดตัวแปรสมาชิกบางตัวนอกเมธอดสมาชิกใด ๆ ตัวแปรสามารถเป็นแบบคงที่หรือไม่คงที่ขึ้นอยู่กับวิธีการแสดงตัวแปร
ตัวอย่างเช่น:
#!/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
เป็นไปได้ที่จะมี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
คุณสามารถบังคับคลาสให้เป็นแบบสแตติกโดยใช้ 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
__new__
พ่อแม่ของมัน ...
จุดหนึ่งที่น่าสนใจมากเกี่ยวกับการค้นหาคุณลักษณะของ 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
เป็นแบบสแตติกในแง่ของการไม่เกี่ยวข้องกับอินสแตนซ์เฉพาะค่ายังคงขึ้นอยู่กับอินสแตนซ์ (คลาสของ)
ในเรื่องที่เกี่ยวกับเรื่องนี้คำตอบสำหรับค่าคงที่ตัวแปรคงที่คุณสามารถใช้บ่ง นี่คือตัวอย่าง:
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สำหรับข้อมูลเพิ่มเติมเกี่ยวกับคำอธิบาย
@property
ซึ่งก็เหมือนกับการใช้ descriptor แต่มันเป็นโค้ดที่น้อยกว่ามาก
ใช่แล้ว 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"
ใช่เป็นไปได้แน่นอนในการเขียนตัวแปรและวิธีการแบบคงที่ในไพ ธ อน
ตัวแปรแบบคงที่: ตัวแปรที่ประกาศในระดับชั้นจะเรียกว่าตัวแปรแบบคงที่ซึ่งสามารถเข้าถึงได้โดยตรงโดยใช้ชื่อชั้น
>>> 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!!
เพื่อหลีกเลี่ยงความสับสนที่อาจเกิดขึ้นฉันต้องการเปรียบเทียบตัวแปรคงที่และวัตถุที่ไม่เปลี่ยนรูปแบบ
วัตถุชนิดดั้งเดิมบางชนิดเช่นจำนวนเต็มลอยสายอักขระและ touples ไม่สามารถเปลี่ยนแปลงได้ใน Python ซึ่งหมายความว่าวัตถุที่อ้างถึงโดยชื่อที่กำหนดไม่สามารถเปลี่ยนแปลงได้หากเป็นวัตถุประเภทใดชนิดหนึ่งดังกล่าวข้างต้น สามารถกำหนดชื่อให้กับวัตถุอื่นได้ แต่วัตถุนั้นอาจไม่สามารถเปลี่ยนแปลงได้
การสร้างตัวแปรแบบสแตติกใช้ขั้นตอนนี้ต่อไปโดยการไม่อนุญาตให้ชื่อตัวแปรชี้ไปที่วัตถุใด ๆ แต่เป็นสิ่งที่ชี้ไปในปัจจุบัน (หมายเหตุ: นี่เป็นแนวคิดซอฟต์แวร์ทั่วไปและไม่เฉพาะเจาะจงกับ Python โปรดดูโพสต์ของผู้อื่นสำหรับข้อมูลเกี่ยวกับการใช้งานสถิติใน Python)
วิธีที่ดีที่สุดที่ฉันพบคือใช้คลาสอื่น คุณสามารถสร้างวัตถุแล้วใช้มันกับวัตถุอื่น ๆ
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
สำหรับใครก็ตามที่ใช้คลาสโรงงานที่มี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
ฉันสงสัยว่านี่คือสิ่งที่ทุกคนหมายถึงโดยตัวแปรคงที่ทั้งหมด
some_var
ไม่เปลี่ยนรูปและกำหนดแบบคงที่หรือไม่? การเข้าถึงภายนอก getter เกี่ยวข้องกับตัวแปรที่เป็นแบบคงที่หรือไม่? ฉันมีคำถามมากมายตอนนี้ ชอบที่จะได้ยินคำตอบบางอย่างเมื่อคุณมีเวลา
some_var
ข้างต้นไม่ใช่สมาชิกชั้นเรียนเลย ใน Python สมาชิกคลาสทั้งหมดสามารถเข้าถึงได้จากนอกชั้นเรียน
nonlocal
keywoard "กระแทก" ขอบเขตของตัวแปร ขอบเขตของคำจำกัดความของคลาสร่างกายเป็นอิสระจากขอบเขตที่พบว่าตัวเองภายในเมื่อคุณพูดnonlocal some_var
ว่าเป็นเพียงการสร้างการอ้างอิงชื่อที่ไม่ใช่ท้องถิ่น (อ่าน: ไม่อยู่ในขอบเขตคำจำกัดความของคลาส) ไปยังวัตถุที่มีชื่ออื่น ดังนั้นจึงไม่ได้แนบมากับคำจำกัดความของคลาสเนื่องจากไม่ได้อยู่ในขอบเขตเนื้อหาของคลาส
ดังนั้นนี่อาจเป็นแฮ็ค แต่ฉันเคยใช้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()
ตัวอย่างเช่นการใช้คลาสเบสแบบนามธรรมหรืออะไรก็ตามที่เป็นเป้าหมาย อย่างไรก็ตามในแบ็กเอนด์มันอาจจะสร้างอินสแตนซ์ในไพ ธ อนและไม่คงที่อย่างแท้จริงเพราะอีวาล์กำลังคืนออบเจกต์ .... ซึ่งจะต้องมีอินสแตนซ์ .... ซึ่งให้พฤติกรรมแบบคงที่
คุณสามารถใช้รายการหรือพจนานุกรมเพื่อรับ "พฤติกรรมแบบคงที่" ระหว่างอินสแตนซ์
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
หากคุณพยายามที่จะแชร์ตัวแปรแบบคงที่เพื่อเพิ่มตัวแปรข้ามอินสแตนซ์อื่น ๆ บางอย่างเช่นสคริปต์นี้ทำงานได้ดี:
# -*- 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)
@classmethod
เหนือกว่า@staticmethod
คือคุณจะได้ชื่อของคลาสที่มีการเรียกใช้เมธอดถึงแม้ว่ามันจะเป็นคลาสย่อยก็ตาม วิธีการแบบสแตติกขาดข้อมูลนี้ดังนั้นจึงไม่สามารถเรียกวิธีการแทนที่ได้ตัวอย่างเช่น