ฉันจะสร้างค่าคงที่ใน Python ได้อย่างไร


989

มีวิธีในการประกาศค่าคงที่ใน Python หรือไม่? ใน Java เราสามารถสร้างค่าคงที่ในลักษณะนี้:

public static final String CONST_NAME = "Name";

อะไรคือสิ่งที่เทียบเท่ากับการประกาศคงที่ Java ข้างต้นใน Python?


6
จริงๆแล้ววิธีการสร้างตัวแปรแบบอ่านอย่างเดียวเป็นไปได้ผ่านฟังก์ชั่นคุณสมบัติ / ตกแต่งภายในของงูใหญ่ คำตอบของInvเป็นตัวอย่างของการใช้งานที่กำหนดเองว่า สถานที่ให้บริการมากขึ้นใช้งานทั่วไปกว่าที่แม้ว่าการวิเคราะห์ที่ดีของวิธีการทำงานอยู่บน Shalabh Chaturvedi ของงูหลามคุณสมบัติและวิธีการ
n611x007

20
IMHO การบังคับใช้ค่าคงที่คือ "ไม่ใช่ pythonic" ใน Python 2.7 คุณสามารถเขียนTrue=Falseและ(2+2==4)==Trueส่งคืนFalseได้
osa

8
ตามคำตอบอื่น ๆ แสดงให้เห็นว่าไม่มีทางหรือไม่จำเป็นต้องประกาศค่าคงที่ แต่คุณอาจอ่านPEPนี้เกี่ยวกับการประชุม เช่น This_IS_A_CONSTANT
Rasika Perera

34
@osa: คุณไม่สามารถทำเช่นนั้นในหลาม 3 SyntaxError: can't assign to keyword- ดูเหมือนว่าสิ่งที่ดี
naught101

3
น่าแปลกใจที่เรื่องนี้ยังไม่ได้รับการกล่าวถึงจนกระทั่งตอนนี้ แต่Enumsน่าจะเป็นวิธีที่ดีในการกำหนดค่าคงที่ที่ระบุ
cs95

คำตอบ:


973

ไม่มีเลย คุณไม่สามารถประกาศตัวแปรหรือค่าเป็นค่าคงที่ใน Python แค่อย่าเปลี่ยนมัน

หากคุณอยู่ในชั้นเรียนค่าเทียบเท่าจะเป็น:

class Foo(object):
    CONST_NAME = "Name"

ถ้าไม่มันเป็นเพียง

CONST_NAME = "Name"

แต่คุณอาจต้องการดูตัวอย่างรหัสค่าคงที่ใน Pythonโดย Alex Martelli


ใน Python 3.8 มีtyping.Finalคำอธิบายประกอบแบบตัวแปรที่จะบอกตัวตรวจสอบชนิดสแตติก (เช่น mypy) ว่าตัวแปรของคุณไม่ควรถูกกำหนดใหม่ นี่คือเทียบเท่าใกล้เคียงกับของ finalJava อย่างไรก็ตามมันไม่ได้ป้องกันการโอนสิทธิ :

from typing import Final

a: Final = 1

# Executes fine, but mypy will report an error if you run mypy on this:
a = 2

27
จากนั้นทำสิ่งที่อยู่ใน "ค่าคงที่ใน Python" คุณควรใช้ฟังก์ชัน "decorator" หรือ decorator
Seth Johnson

26
ผู้คนถามเกี่ยวกับคุณสมบัติเดียวกันใน Perl มีโมดูลนำเข้าที่เรียกว่า "ใช้ค่าคงที่" แต่ (AFAIK) เป็นเพียงตัวห่อหุ้มเพื่อสร้างฟังก์ชันเล็ก ๆ ที่ส่งกลับค่า ฉันทำเช่นเดียวกันใน Python ตัวอย่าง:def MY_CONST_VALUE(): return 123
kevinarpe

8
"ไม่มีไม่มี" จริง แต่การสร้างงานของคนอื่นฉันได้เพิ่มคำตอบด้านล่างด้วยการใช้ "ค่าคงที่" สั้น ๆ และง่ายสำหรับ python 2.7 (ซึ่งขาด "enum") เหล่านี้เป็นแบบอ่านอย่างเดียว enum name.attributeและสามารถมีค่าใด ๆ การประกาศเป็นเรื่องง่ายNums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0)การใช้งานตรงไปตรงมาprint 10 + Nums.PIพยายามเปลี่ยนผลลัพธ์เป็นข้อยกเว้นNums.PI = 22=> ValueError (.. )
ToolmakerSteve

108
แค่อย่าเปลี่ยนมัน คุณทำให้วันของฉัน
สวัสดีแองเจิล

89
"อย่าเปลี่ยนเลย" ไม่เป็นประโยชน์เลย ไม่ตอบคำถามและฉันขอแนะนำให้ลบออก
Bartek Banachewicz

354

ไม่มีconstคำหลักเหมือนในภาษาอื่น ๆ อย่างไรก็ตามมันเป็นไปได้ที่จะสร้างคุณสมบัติที่มี "ฟังก์ชั่น getter"เพื่ออ่านข้อมูล แต่ไม่มี "ฟังก์ชั่น setter"เพื่อเขียนข้อมูลอีกครั้ง สิ่งนี้จะช่วยป้องกันตัวระบุจากการเปลี่ยนแปลง

นี่คือการใช้ทางเลือกโดยใช้คุณสมบัติคลาส:

โปรดทราบว่ารหัสนั้นไม่ง่ายสำหรับผู้อ่านที่สงสัยเกี่ยวกับค่าคงที่ ดูคำอธิบายด้านล่าง

def constant(f):
    def fset(self, value):
        raise TypeError
    def fget(self):
        return f()
    return property(fget, fset)

class _Const(object):
    @constant
    def FOO():
        return 0xBAADFACE
    @constant
    def BAR():
        return 0xDEADBEEF

CONST = _Const()

print CONST.FOO
##3131964110

CONST.FOO = 0
##Traceback (most recent call last):
##    ...
##    CONST.FOO = 0
##TypeError: None

คำอธิบายรหัส:

  1. กำหนดฟังก์ชั่น constantที่รับนิพจน์และใช้เพื่อสร้าง "getter" - ฟังก์ชั่นที่คืนค่าของนิพจน์เท่านั้น
  2. ฟังก์ชันตัวตั้งค่ายก TypeError ดังนั้นจึงเป็นแบบอ่านอย่างเดียว
  3. ใช้constantฟังก์ชั่นที่เราเพิ่งสร้างเป็นของตกแต่งเพื่อกำหนดคุณสมบัติแบบอ่านอย่างเดียวได้อย่างรวดเร็ว

และในแบบที่ล้าสมัยกว่า:

(รหัสค่อนข้างซับซ้อนและมีคำอธิบายเพิ่มเติมด้านล่าง)

class _Const(object):
    @apply
    def FOO():
        def fset(self, value):
            raise TypeError
        def fget(self):
            return 0xBAADFACE
        return property(**locals())

CONST = _Const()

print CONST.FOO
##3131964110

CONST.FOO = 0
##Traceback (most recent call last):
##    ...
##    CONST.FOO = 0
##TypeError: None

โปรดทราบว่า @apply decorator ดูเหมือนจะเลิกใช้แล้ว

  1. เพื่อกำหนดตัวระบุ FOO, ภาคเรียนที่สองกำหนดฟังก์ชั่น (fset, fget - ชื่ออยู่ที่ฉันเลือก)
  2. จากนั้นใช้propertyฟังก์ชันในตัวเพื่อสร้างวัตถุที่สามารถเป็น "set" หรือ "get"
  3. หมายเหตุหมวกpropertyฟังก์ชั่นของพารามิเตอร์สองคนแรกที่มีชื่อและfsetfget
  4. ใช้ความจริงที่ว่าเราเลือกชื่อเหล่านี้มากสำหรับ getter & setter ของเราและสร้างพจนานุกรมคำหลักโดยใช้ ** (เครื่องหมายดอกจันคู่) ที่ใช้กับคำจำกัดความทั้งหมดของขอบเขตนั้นเพื่อส่งผ่านพารามิเตอร์ไปยังpropertyฟังก์ชัน

11
ขึ้นอยู่กับเอกสารบนAttributeErrorและTypeErrorผมคิดว่าข้อยกเว้นยกควรจะเป็นข้อผิดพลาดใหม่ซึ่งผมเสนอตั้งชื่อConstantErrorหรือสิ่งที่ต้องการซึ่งเป็น subclass TypeErrorของ ส่วนในเอกสารที่ทำให้ฉันคิดว่า: docs.python.org/2/library/exceptions.html
ArtOfWarfare

3
ฉันประหลาดใจกับรหัสนี้ ทำไมแบบอักษรวิธีการ FOO () และ BAR () มีเหตุผลเป็นตัวเอง? IDE ของฉันขีดเส้นใต้เครื่องหมายวงเล็บบนข้อผิดพลาดสีแดง ("คอมไพล์") ฉันเหนื่อยที่จะใส่ตัวเอง แต่แล้วฉันก็พบข้อผิดพลาด
3770060

10
การไปที่ความยาวเหล่านี้จะทำให้ร่างข้อบกพร่องชัดเจนในภาษาไพ ธ อน ทำไมพวกเขาถึงไม่รู้สึกว่าจำเป็นต้องเพิ่มสิ่งนี้ลงใน Python 3 ฉันไม่อยากจะเชื่อเลยว่าไม่มีใครแนะนำเลยและฉันก็ไม่สามารถมองเห็นเหตุผลที่อยู่เบื้องหลังคณะกรรมการบางคณะที่ยังคงอยู่ใช่ไหม? nah.
Andrew S

8
และโซลูชันของคุณยังสามารถแก้ไขได้โดยโปรแกรมเมอร์ python ที่กำหนดโดยใช้CONST.__dict__['FOO'] = 7
pppery

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

112

ใน Python แทนที่จะใช้ภาษาบังคับบางอย่างคนใช้ระเบียบการตั้งชื่อเช่น__methodสำหรับวิธีส่วนตัวและใช้_methodสำหรับวิธีที่ได้รับการป้องกัน

ดังนั้นในลักษณะเดียวกันคุณสามารถประกาศค่าคงที่ได้เช่นตัวพิมพ์ใหญ่ทั้งหมด

MY_CONSTANT = "one"

หากคุณต้องการให้ค่าคงที่นี้ไม่มีการเปลี่ยนแปลงคุณสามารถขอเข้าใช้การเข้าถึงคุณลักษณะและเล่นกลได้ แต่วิธีการที่ง่ายกว่าคือการประกาศฟังก์ชัน

def MY_CONSTANT():
    return "one"

มีเพียงปัญหาอยู่ทุกที่ที่คุณต้องทำ MY_CONSTANT () แต่อีกวิธีMY_CONSTANT = "one"คือวิธีที่ถูกต้องใน python (โดยปกติ)

คุณยังสามารถใช้namedtupleเพื่อสร้างค่าคงที่:

>>> from collections import namedtuple
>>> Constants = namedtuple('Constants', ['pi', 'e'])
>>> constants = Constants(3.14, 2.718)
>>> constants.pi
3.14
>>> constants.pi = 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

17
การทำdef MY_CONSTANT(): return "one"จะไม่หยุดใครบางคนในภายหลังในรหัสทำMY_CONSTANT = "two"(หรือการประกาศฟังก์ชัน)
Matthew Schinckel

6
@MatthewSchinckel มันเกี่ยวกับการประชุมการเปลี่ยน MY_CONSTANT จะไม่เปลี่ยนการใช้งาน MY_CONSTANT () แต่จะทำให้เกิดข้อผิดพลาดและในงูใหญ่ถ้าคุณต้องการคุณสามารถเปลี่ยนแปลงอะไรก็ได้ไม่มีเคล็ดลับที่ฉลาดสามารถปกป้องคุณได้
Anurag Uniyal

3
ขอขอบคุณที่แสดงวิธีการตั้งชื่อเป็นทูเปิล นวัตกรรมอย่างแน่นอน คุณอาจพบว่า"ความคิดเห็น"ของฉันมีความเกี่ยวข้องที่นี่
RayLuo

@ Matthewschinckel คุณสามารถกำหนดสิ่งใดก็ได้ใน python ดังนั้นมันจึงไม่ใช่จุดที่ดีจริงๆ
cslotty

@ MatthewSchinckel ความคิดไม่ได้เขียนMY_CONSTANT = MY_CONSTANT()แต่ใช้MY_CONSTANT()เป็นค่าคงที่ แน่นอนสิ่งนี้ แต่สิ่งนี้ดีและเป็นไปตามหลักการของงูใหญ่ "เราเป็นผู้ใหญ่ทุกคนที่นี่" - เช่นนักพัฒนาจะไม่ได้รับอนุญาตให้ตัดสินใจที่จะแทนที่กฎเมื่อพวกเขามีเหตุผลที่ดีและรู้ว่าพวกเขากำลังทำอะไร
jonathan.scholbach

69

เมื่อเร็ว ๆ นี้ฉันได้พบการอัปเดตที่กระชับมากซึ่งทำให้ข้อความแสดงข้อผิดพลาดที่มีความหมายและป้องกันการเข้าถึงผ่าน__dict__:

class CONST(object):
    __slots__ = ()
    FOO = 1234

CONST = CONST()

# ----------

print(CONST.FOO)    # 1234

CONST.FOO = 4321              # AttributeError: 'CONST' object attribute 'FOO' is read-only
CONST.__dict__['FOO'] = 4321  # AttributeError: 'CONST' object has no attribute '__dict__'
CONST.BAR = 5678              # AttributeError: 'CONST' object has no attribute 'BAR'

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

แก้ไข - โซลูชันดั้งเดิม

ฉันอาจจะพลาดที่นี่ แต่ดูเหมือนว่าจะได้ผลสำหรับฉัน:

class CONST(object):
    FOO = 1234

    def __setattr__(self, *_):
        pass

CONST = CONST()

#----------

print CONST.FOO    # 1234

CONST.FOO = 4321
CONST.BAR = 5678

print CONST.FOO    # Still 1234!
print CONST.BAR    # Oops AttributeError

การสร้างอินสแตนซ์ช่วยให้__setattr__วิธีเวทย์มนตร์เตะและสกัดกั้นความพยายามในการตั้งค่าFOOตัวแปร คุณสามารถโยนข้อยกเว้นที่นี่ถ้าคุณต้องการ การสร้างอินสแตนซ์เหนือชื่อคลาสป้องกันการเข้าถึงโดยตรงผ่านคลาส

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


11
นี่คือคำตอบที่ดีที่สุดและชัดเจนที่สุดเพราะมันมี "กลไก" น้อยที่สุด แต่เป็นฟังก์ชันการทำงานส่วนใหญ่ การเพิ่มข้อยกเว้นเป็นสิ่งสำคัญแม้ว่า ... ไม่ใช่ตัวเลือก
Erik Aronesty

ฉันใช้เส้นทางที่สั้นกว่าซึ่งจะสร้างข้อผิดพลาดที่มีความหมายโดยอัตโนมัติ แต่มีสไตล์เดียวกันมาก ฉันทิ้งความคิดดั้งเดิมไว้ที่นี่เพื่อเปรียบเทียบ
Jon Betts

ช่างน่าเสียดายที่คุณยังต้องการCONST.คำนำหน้านี้อยู่ นอกจากนี้ในสถานการณ์ที่มีหลายโมดูลสิ่งนี้จะซับซ้อน
Alfe

1
ฉันคิดว่าโดยทั่วไปแล้วคุณต้องการจัดกลุ่มค่าคงที่ให้เป็นกลุ่มที่เกี่ยวข้องอยู่แล้วในสถานการณ์นั้น (แทนที่จะมีวัตถุ CONST ขนาดยักษ์หนึ่งชิ้น) ดังนั้นจึงอาจไม่ใช่สิ่งที่เลวร้าย
Jon Betts

ทำไมคำตอบนี้ยังคงไกลขนาดนี้! __slots__วิธีการแก้ปัญหานั้นสวยงามและมีประสิทธิภาพ จากทุกสิ่งที่ฉันได้อ่านสิ่งนี้ใกล้เคียงกับการสร้างค่าคงที่ใน Python ขอบคุณมาก. และสำหรับทุกคนที่สนใจนี่คือคำอธิบายที่__slots__วิเศษและในเชิงลึกของเวทมนตร์
JohnGalt

34

Python ไม่มีค่าคงที่

บางทีทางเลือกที่ง่ายที่สุดคือการกำหนดฟังก์ชั่น:

def MY_CONSTANT():
    return 42

MY_CONSTANT() ตอนนี้มีฟังก์ชันทั้งหมดของค่าคงที่ (รวมทั้งเครื่องหมายวงเล็บบางอันที่น่ารำคาญ)


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

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

@MrMesees แก้ไขค่าส่งคืนหรือไม่ คุณหมายถึงการแก้ไขต้นฉบับหรือไม่ แต่จากนี้คุณจะไม่ได้รับการปกป้องแม้ใน C ++ ที่ค่าคงที่ (เช่นconstexpr) เป็นค่าคงที่ที่แท้จริง
Ruslan

@Ruslan สิ่งที่ฉันหมายถึงคือเนื่องจาก python ไม่มี constexpr มันจะไม่หยุดค่าที่ถูกแก้ไขหลังจากถูกส่งกลับไปยังบริบทภายนอก ไม่มีการดำเนินการใด ๆ กับ 42 เพื่อบังคับใช้สถานะแช่แข็งในตัวอย่างนี้
MrMesees

20

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

>>> from named_constants import Constants
>>> class Colors(Constants):
...     black = 0
...     red = 1
...     white = 15
...
>>> c = Colors.black
>>> c == 0
True
>>> c
Colors.black
>>> c.name()
'black'
>>> Colors(0) is c
True

นี่เป็น Python ขั้นสูงกว่าเล็กน้อย แต่ยังใช้งานง่ายและสะดวก (โมดูลมีคุณสมบัติเพิ่มเติมบางอย่างรวมถึงค่าคงที่ที่อ่านได้อย่างเดียวดู README)

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


3
ฉันชอบการนำของคุณไปใช้กับ GitHub ฉันเกือบจะพร้อมที่จะเขียนคลาสพื้นฐานซึ่งใช้ฟังก์ชั่นการค้นหาแบบย้อนกลับ แต่ฉันเห็นว่าคุณทำอย่างนั้นแล้ว!
Kerr

ขอบคุณ @Kerr มันเป็นคำติชมแรกที่ฉันได้รับและทำให้ฉันมีความสุข :-)
hans_meine

น่ากลัว ฉันเพิ่งลองสิ่งนี้ ยินดีที่ได้เป็นตัวเลือก แม้ว่าฉันจะยังไม่ได้ตัดสินใจว่าฉันสนใจเรื่องอ่านอย่างเดียวเพียงพอหรือไม่เพื่อใช้สิ่งนี้แทนที่จะทำdef enum(**enums): return type('Enum', (), enums)อย่างนั้น Numbers = enum(ONE=1, TWO=2, THREE='three')ตามstackoverflow.com/a/1695250/199364หัวข้อ "ในเวอร์ชันก่อนหน้า ... "
ToolmakerSteve

19

คุณสมบัติเป็นวิธีหนึ่งในการสร้างค่าคงที่ คุณสามารถทำได้โดยการประกาศคุณสมบัติ getter แต่ไม่สนใจ setter ตัวอย่างเช่น:

class MyFinalProperty(object):

    @property
    def name(self):
        return "John"

คุณสามารถดูบทความที่ฉันเขียนขึ้นเพื่อค้นหาวิธีใช้คุณสมบัติ Python


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

18

แก้ไข: เพิ่มรหัสตัวอย่างสำหรับ Python 3

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

ก่อนอื่นให้สร้างเมตาคลาส :

class MetaConst(type):
    def __getattr__(cls, key):
        return cls[key]

    def __setattr__(cls, key, value):
        raise TypeError

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

class Const(object):
    __metaclass__ = MetaConst

    def __getattr__(self, name):
        return self[name]

    def __setattr__(self, name, value):
        raise TypeError

หรือถ้าคุณใช้ Python 3:

class Const(object, metaclass=MetaConst):
    def __getattr__(self, name):
        return self[name]

    def __setattr__(self, name, value):
        raise TypeError

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

class MyConst(Const):
    A = 1
    B = 2

ตอนนี้อุปกรณ์ประกอบฉากเข้าถึงได้โดยตรงหรือผ่านอินสแตนซ์ควรจะคงที่:

MyConst.A
# 1
my_const = MyConst()
my_const.A
# 1

MyConst.A = 'changed'
# TypeError
my_const.A = 'changed'
# TypeError

นี่คือตัวอย่างของการกระทำข้างต้น นี่เป็นอีกตัวอย่างสำหรับ Python 3


10

คุณสามารถใช้ namedtuple เป็นวิธีแก้ปัญหาเพื่อสร้างค่าคงที่ที่ทำงานในลักษณะเดียวกับตัวแปรสแตติกสุดท้ายใน Java (Java "ค่าคงที่") ได้อย่างมีประสิทธิภาพ เป็นวิธีการแก้ปัญหาไปมันสวยงาม (แนวทางที่สวยงามกว่านี้ก็คือการปรับปรุงภาษา Python - ภาษาประเภทใดที่ให้คุณกำหนดใหม่ได้math.pi- แต่ฉันพูดนอกเรื่อง)

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

ตัวอย่างต่อไปนี้คุณจะจำได้ว่าในชวาเราต้องกำหนดอย่างต่อเนื่องภายในชั้นบาง ; เพราะคุณไม่ได้พูดถึงชื่อชั้น, Fooขอเรียกว่า นี่คือคลาส Java:

public class Foo {
  public static final String CONST_NAME = "Name";
}

นี่คือ Python ที่เทียบเท่ากัน

from collections import namedtuple
Foo = namedtuple('_Foo', 'CONST_NAME')('Name')

จุดสำคัญที่ฉันต้องการเพิ่มที่นี่คือคุณไม่จำเป็นต้องมีFooประเภทแยกต่างหาก("tuple ที่ไม่ระบุชื่อแบบไม่ระบุชื่อ" จะดีแม้ว่าถึงแม้ว่าจะฟังดูเหมือน oxymoron ก็ตาม) ดังนั้นเราจึงตั้งชื่อ tuple ของเรา_Fooเพื่อหวังว่ามันจะไม่ หลบหนีจากการนำเข้าโมดูล

จุดที่สองที่นี่คือการที่เราได้ทันทีสร้างอินสแตนซ์ของ nametuple เรียกมันว่าFoo; ไม่จำเป็นต้องทำสิ่งนี้ในขั้นตอนแยกต่างหาก (เว้นแต่คุณต้องการ) ตอนนี้คุณสามารถทำสิ่งที่คุณสามารถทำได้ใน Java:

>>> Foo.CONST_NAME
'Name'

แต่คุณไม่สามารถกำหนดได้:

>>> Foo.CONST_NAME = 'bar'

AttributeError: can't set attribute

กิตติกรรมประกาศ: ฉันคิดว่าฉันคิดค้นวิธีการตั้งชื่อชื่อ แต่ฉันเห็นว่ามีคนอื่นให้คำตอบคล้ายกัน จากนั้นฉันก็สังเกตเห็น"tuples ชื่อ" ใน Python คืออะไร ซึ่งชี้ให้เห็นว่าsys.version_infoตอนนี้เป็น tuple ที่มีชื่อดังนั้นบางทีไลบรารี Python มาตรฐานจะเกิดขึ้นกับแนวคิดนี้ก่อนหน้านี้มาก

โปรดทราบว่าน่าเสียดาย (นี่ยังคงเป็น Python) คุณสามารถลบการFooกำหนดทั้งหมดได้ทั้งหมด:

>>> Foo = 'bar'

(facepalm)

แต่อย่างน้อยเราก็ป้องกันไม่ให้Foo.CONST_NAMEคุณค่าเปลี่ยนแปลงและนั่นก็ดีกว่าไม่มีอะไรเลย โชคดี.


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

10

PEP 591มีคุณสมบัติ 'รอบสุดท้าย' การบังคับใช้ลงไปที่ตัวตรวจสอบชนิด

ดังนั้นคุณสามารถทำได้:

MY_CONSTANT: Final = 12407

หมายเหตุ: Finalคำหลักใช้ได้เฉพาะกับรุ่น Python 3.8 เท่านั้น


9

นี่คือการใช้งานคลาส "ค่าคงที่" ซึ่งสร้างอินสแตนซ์ที่มีแอตทริบิวต์แบบอ่านอย่างเดียว (คงที่) เช่นสามารถใช้Nums.PIเพื่อรับค่าที่ได้รับการเริ่มต้นเป็น3.14159และNums.PI = 22ยกข้อยกเว้น

# ---------- Constants.py ----------
class Constants(object):
    """
    Create objects with read-only (constant) attributes.
    Example:
        Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0)
        print 10 + Nums.PI
        print '----- Following line is deliberate ValueError -----'
        Nums.PI = 22
    """

    def __init__(self, *args, **kwargs):
        self._d = dict(*args, **kwargs)

    def __iter__(self):
        return iter(self._d)

    def __len__(self):
        return len(self._d)

    # NOTE: This is only called if self lacks the attribute.
    # So it does not interfere with get of 'self._d', etc.
    def __getattr__(self, name):
        return self._d[name]

    # ASSUMES '_..' attribute is OK to set. Need this to initialize 'self._d', etc.
    #If use as keys, they won't be constant.
    def __setattr__(self, name, value):
        if (name[0] == '_'):
            super(Constants, self).__setattr__(name, value)
        else:
            raise ValueError("setattr while locked", self)

if (__name__ == "__main__"):
    # Usage example.
    Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0)
    print 10 + Nums.PI
    print '----- Following line is deliberate ValueError -----'
    Nums.PI = 22

ต้องขอบคุณFrozenDict ของ @MikeGrahamซึ่งฉันใช้เป็นจุดเริ่มต้น การเปลี่ยนแปลงดังนั้นแทนที่จะไวยากรณ์การใช้งานคือNums['ONE']Nums.ONE

และต้องขอบคุณคำตอบของ @ Raufio สำหรับแนวคิดที่จะแทนที่ __ setattr __

หรือสำหรับการดำเนินการกับการทำงานเพิ่มเติมโปรดดูที่ @Hans_meine 's named_constants ที่ GitHub


2
Python เป็นภาษาที่ให้ความยินยอมแก่ผู้ใหญ่ ไม่มีการป้องกันอะไรแบบนี้ Nums._d['PI'] = 22 ฉันเชื่อว่าภาษานั้นไม่มีวิธีในการทำเครื่องหมายสิ่งต่าง ๆ ว่าเป็นสิ่งที่ไม่แน่นอน
Ajay M

8

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

my_tuple = (0 """Or any other value""",)

ในการตรวจสอบค่าของตัวแปรใช้สิ่งที่คล้ายกับสิ่งนี้:

if my_tuple[0] == 0:
    #Code goes here

หากคุณพยายามที่จะเปลี่ยนค่านี้ข้อผิดพลาดจะถูกยกขึ้น


7

ฉันจะสร้างคลาสที่แทนที่__setattr__เมธอดของคลาสอ็อบเจ็กต์พื้นฐานและตัดค่าคงที่ของฉันด้วยนั้นโปรดทราบว่าฉันใช้ python 2.7:

class const(object):
    def __init__(self, val):
        super(const, self).__setattr__("value", val)
    def __setattr__(self, name, val):
        raise ValueError("Trying to change a constant value", self)

วิธีตัดสตริง:

>>> constObj = const("Try to change me")
>>> constObj.value
'Try to change me'
>>> constObj.value = "Changed"
Traceback (most recent call last):
   ...
ValueError: Trying to change a constant value
>>> constObj2 = const(" or not")
>>> mutableObj = constObj.value + constObj2.value
>>> mutableObj #just a string
'Try to change me or not'

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


+1 สำหรับแนวทางที่น่าสนใจ แม้ว่าจะไม่สะอาดเท่าคำตอบที่ได้รับมาแล้ว และแม้แต่โซลูชันที่แนะนำก่อนหน้านี้ที่ง่ายที่สุดdef ONE(): return 1ก็คือใช้งานง่ายONE()กว่าคำตอบONE.valueนี้
ToolmakerSteve

7

น่าเสียดายที่ Python ยังไม่มีค่าคงที่อยู่และมันก็น่าละอาย ES6 ได้เพิ่มค่าคงที่การสนับสนุนใน JavaScript แล้ว ( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/const ) เนื่องจากเป็นสิ่งที่มีประโยชน์มากในภาษาการเขียนโปรแกรมใด ๆ ตามที่ได้รับคำตอบในคำตอบอื่น ๆ ในชุมชน Python ให้ใช้การประชุม - ตัวแปรตัวพิมพ์ใหญ่ของผู้ใช้เป็นค่าคงที่ แต่ไม่ได้ป้องกันข้อผิดพลาดโดยพลการในรหัส ถ้าคุณชอบคุณอาจพบว่ามีประโยชน์ในการแก้ปัญหาไฟล์เดียวเช่นเดียวกับต่อไป (ดูเอกสารวิธีการใช้งาน)

ไฟล์ constants.py

import collections


__all__ = ('const', )


class Constant(object):
    """
    Implementation strict constants in Python 3.

    A constant can be set up, but can not be changed or deleted.
    Value of constant may any immutable type, as well as list or set.
    Besides if value of a constant is list or set, it will be converted in an immutable type as next:
        list -> tuple
        set -> frozenset
    Dict as value of a constant has no support.

    >>> const = Constant()
    >>> del const.temp
    Traceback (most recent call last):
    NameError: name 'temp' is not defined
    >>> const.temp = 1
    >>> const.temp = 88
    Traceback (most recent call last):
        ...
    TypeError: Constanst can not be changed
    >>> del const.temp
    Traceback (most recent call last):
        ...
    TypeError: Constanst can not be deleted
    >>> const.I = ['a', 1, 1.2]
    >>> print(const.I)
    ('a', 1, 1.2)
    >>> const.F = {1.2}
    >>> print(const.F)
    frozenset([1.2])
    >>> const.D = dict()
    Traceback (most recent call last):
        ...
    TypeError: dict can not be used as constant
    >>> del const.UNDEFINED
    Traceback (most recent call last):
        ...
    NameError: name 'UNDEFINED' is not defined
    >>> const()
    {'I': ('a', 1, 1.2), 'temp': 1, 'F': frozenset([1.2])}
    """

    def __setattr__(self, name, value):
        """Declaration a constant with value. If mutable - it will be converted to immutable, if possible.
        If the constant already exists, then made prevent againt change it."""

        if name in self.__dict__:
            raise TypeError('Constanst can not be changed')

        if not isinstance(value, collections.Hashable):
            if isinstance(value, list):
                value = tuple(value)
            elif isinstance(value, set):
                value = frozenset(value)
            elif isinstance(value, dict):
                raise TypeError('dict can not be used as constant')
            else:
                raise ValueError('Muttable or custom type is not supported')
        self.__dict__[name] = value

    def __delattr__(self, name):
        """Deny against deleting a declared constant."""

        if name in self.__dict__:
            raise TypeError('Constanst can not be deleted')
        raise NameError("name '%s' is not defined" % name)

    def __call__(self):
        """Return all constans."""

        return self.__dict__


const = Constant()


if __name__ == '__main__':
    import doctest
    doctest.testmod()

หากยังไม่เพียงพอให้ดูตัวอย่างเต็มรูปแบบ

import decimal
import uuid
import datetime
import unittest

from ..constants import Constant


class TestConstant(unittest.TestCase):
    """
    Test for implementation constants in the Python
    """

    def setUp(self):

        self.const = Constant()

    def tearDown(self):

        del self.const

    def test_create_constant_with_different_variants_of_name(self):

        self.const.CONSTANT = 1
        self.assertEqual(self.const.CONSTANT, 1)
        self.const.Constant = 2
        self.assertEqual(self.const.Constant, 2)
        self.const.ConStAnT = 3
        self.assertEqual(self.const.ConStAnT, 3)
        self.const.constant = 4
        self.assertEqual(self.const.constant, 4)
        self.const.co_ns_ta_nt = 5
        self.assertEqual(self.const.co_ns_ta_nt, 5)
        self.const.constant1111 = 6
        self.assertEqual(self.const.constant1111, 6)

    def test_create_and_change_integer_constant(self):

        self.const.INT = 1234
        self.assertEqual(self.const.INT, 1234)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.INT = .211

    def test_create_and_change_float_constant(self):

        self.const.FLOAT = .1234
        self.assertEqual(self.const.FLOAT, .1234)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.FLOAT = .211

    def test_create_and_change_list_constant_but_saved_as_tuple(self):

        self.const.LIST = [1, .2, None, True, datetime.date.today(), [], {}]
        self.assertEqual(self.const.LIST, (1, .2, None, True, datetime.date.today(), [], {}))

        self.assertTrue(isinstance(self.const.LIST, tuple))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.LIST = .211

    def test_create_and_change_none_constant(self):

        self.const.NONE = None
        self.assertEqual(self.const.NONE, None)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.NONE = .211

    def test_create_and_change_boolean_constant(self):

        self.const.BOOLEAN = True
        self.assertEqual(self.const.BOOLEAN, True)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.BOOLEAN = False

    def test_create_and_change_string_constant(self):

        self.const.STRING = "Text"
        self.assertEqual(self.const.STRING, "Text")

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.STRING += '...'

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.STRING = 'TEst1'

    def test_create_dict_constant(self):

        with self.assertRaisesRegexp(TypeError, 'dict can not be used as constant'):
            self.const.DICT = {}

    def test_create_and_change_tuple_constant(self):

        self.const.TUPLE = (1, .2, None, True, datetime.date.today(), [], {})
        self.assertEqual(self.const.TUPLE, (1, .2, None, True, datetime.date.today(), [], {}))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.TUPLE = 'TEst1'

    def test_create_and_change_set_constant(self):

        self.const.SET = {1, .2, None, True, datetime.date.today()}
        self.assertEqual(self.const.SET, {1, .2, None, True, datetime.date.today()})

        self.assertTrue(isinstance(self.const.SET, frozenset))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.SET = 3212

    def test_create_and_change_frozenset_constant(self):

        self.const.FROZENSET = frozenset({1, .2, None, True, datetime.date.today()})
        self.assertEqual(self.const.FROZENSET, frozenset({1, .2, None, True, datetime.date.today()}))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.FROZENSET = True

    def test_create_and_change_date_constant(self):

        self.const.DATE = datetime.date(1111, 11, 11)
        self.assertEqual(self.const.DATE, datetime.date(1111, 11, 11))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.DATE = True

    def test_create_and_change_datetime_constant(self):

        self.const.DATETIME = datetime.datetime(2000, 10, 10, 10, 10)
        self.assertEqual(self.const.DATETIME, datetime.datetime(2000, 10, 10, 10, 10))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.DATETIME = None

    def test_create_and_change_decimal_constant(self):

        self.const.DECIMAL = decimal.Decimal(13123.12312312321)
        self.assertEqual(self.const.DECIMAL, decimal.Decimal(13123.12312312321))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.DECIMAL = None

    def test_create_and_change_timedelta_constant(self):

        self.const.TIMEDELTA = datetime.timedelta(days=45)
        self.assertEqual(self.const.TIMEDELTA, datetime.timedelta(days=45))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.TIMEDELTA = 1

    def test_create_and_change_uuid_constant(self):

        value = uuid.uuid4()
        self.const.UUID = value
        self.assertEqual(self.const.UUID, value)

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.UUID = []

    def test_try_delete_defined_const(self):

        self.const.VERSION = '0.0.1'
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be deleted'):
            del self.const.VERSION

    def test_try_delete_undefined_const(self):

        with self.assertRaisesRegexp(NameError, "name 'UNDEFINED' is not defined"):
            del self.const.UNDEFINED

    def test_get_all_defined_constants(self):

        self.assertDictEqual(self.const(), {})

        self.const.A = 1
        self.assertDictEqual(self.const(), {'A': 1})

        self.const.B = "Text"
        self.assertDictEqual(self.const(), {'A': 1, 'B': "Text"})

ข้อดี: 1. การเข้าถึงค่าคงที่ทั้งหมดสำหรับโครงการทั้งหมด 2. การควบคุมค่าคงที่อย่างเข้มงวด

ขาด: 1. ไม่รองรับประเภทที่กำหนดเองและประเภท 'dict'

หมายเหตุ:

  1. ทดสอบกับ Python3.4 และ Python3.5 (ฉันใช้ 'พิษ' สำหรับมัน)

  2. สภาพแวดล้อมการทดสอบ:

.

$ uname -a
Linux wlysenko-Aspire 3.13.0-37-generic #64-Ubuntu SMP Mon Sep 22 21:28:38 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

คุณสามารถปรับปรุงสิ่งนี้ได้เล็กน้อยโดยการแปลงพจนานุกรมเป็นชื่ออันดับอย่างอัตโนมัติ
Peter Schorn

6

วิธี Pythonic ของการประกาศ "ค่าคงที่" นั้นเป็นตัวแปรระดับโมดูล:

RED = 1
GREEN = 2
BLUE = 3

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

RED = 2เว้นแต่ของหลักสูตรถ้าคุณกำหนดอย่างชัดเจน


21
ใช่ แต่การปิดกั้นความสามารถในการ "ตั้งค่าอย่างชัดเจนRED = 2" เป็นประโยชน์ทั้งหมด (ในภาษาอื่น ๆ ) ที่สามารถประกาศชื่อตัวแปรให้เป็น "คงที่"!
ToolmakerSteve

6
คุณจะได้รับประโยชน์อะไรจากการบล็อกสิ่งนั้น สิ่งที่มีประโยชน์มากที่สุดเกี่ยวกับ const คือการเพิ่มประสิทธิภาพคอมไพเลอร์ซึ่งไม่ได้เป็นเรื่องจริงใน Python ต้องการบางสิ่งบางอย่างที่จะคงที่? แค่อย่าเปลี่ยนมัน หากคุณกังวลเกี่ยวกับการที่คนอื่นเปลี่ยนคุณสามารถวางไว้นอกขอบเขตของพวกเขาหรือเพิ่งรู้ว่าถ้าใครบางคนเปลี่ยนมันนั่นคือปัญหาของพวกเขาและพวกเขาต้องจัดการกับมันไม่ใช่คุณ
Kevin

@Kevin: " คุณจะได้รับประโยชน์ ... " ประโยชน์ของstaticการมีที่เก็บข้อมูลเดียวสำหรับค่าสำหรับอินสแตนซ์ทั้งหมดของชั้นเรียนหรือไม่ เว้นแต่จะมีความเป็นไปได้ที่จะประกาศตัวแปรสแตติก / คลาสแน่นอน
นาที

8
ปัญหารากคือบางคนอาจเห็นว่ามันเป็นค่าที่เป็นแหล่งของความจริงไม่สามารถเปลี่ยนแปลงได้และใช้เป็นแหล่งที่มาของความจริงตลอดทั้งรหัสแทนที่จะแนะนำค่ามายากล (ซึ่งฉันเห็นจำนวนมากใน Python) - และคนอื่น ๆ อาจเห็นว่าเป็นสิ่งที่พวกเขาได้รับอนุญาตให้เปลี่ยนแปลงได้ตามต้องการ เมื่อมีคนเปลี่ยนตัวแปรทั่วโลกและคุณไม่สามารถบอกได้ว่ามันเปลี่ยนแปลงที่ไหนและแอพพลิเคชั่นขัดข้องเพราะ RED = "blue" แทนที่จะเป็น "red" คุณกำลังแนะนำปัญหาที่ไม่จำเป็นทั้งหมดซึ่งได้แก้ไขไปแล้วโดยง่าย เป็นที่เข้าใจในระดับสากล
Dagrooms

5

เราสามารถสร้างวัตถุ descriptor

class Constant:
  def __init__(self,value=None):
    self.value = value
  def __get__(self,instance,owner):
    return self.value
  def __set__(self,instance,value):
    raise ValueError("You can't change a constant")

1) ถ้าเราต้องการทำงานกับค่าคงที่ในระดับอินสแตนซ์แล้ว:

class A:
  NULL = Constant()
  NUM = Constant(0xFF)

class B:
  NAME = Constant('bar')
  LISTA = Constant([0,1,'INFINITY'])

>>> obj=A()
>>> print(obj.NUM)  #=> 255
>>> obj.NUM =100

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: You can't change a constant

2) ถ้าเราต้องการสร้างค่าคงที่เฉพาะในระดับชั้นเรียนเราสามารถใช้ metaclass ที่ทำหน้าที่เป็นภาชนะสำหรับค่าคงที่ของเรา (วัตถุอธิบายของเรา); คลาสทั้งหมดที่สืบทอดมาจะสืบทอดค่าคงที่ของเรา (วัตถุตัวอธิบายของเรา) โดยไม่มีความเสี่ยงใด ๆ ที่สามารถแก้ไขได้

# metaclass of my class Foo
class FooMeta(type): pass

# class Foo
class Foo(metaclass=FooMeta): pass

# I create constants in my metaclass
FooMeta.NUM = Constant(0xff)
FooMeta.NAME = Constant('FOO')

>>> Foo.NUM   #=> 255
>>> Foo.NAME  #=> 'FOO'
>>> Foo.NUM = 0 #=> ValueError: You can't change a constant

ถ้าฉันสร้างคลาสย่อยของ Foo คลาสนี้จะสืบทอดค่าคงที่โดยไม่สามารถแก้ไขได้

class Bar(Foo): pass

>>> Bar.NUM  #=> 255
>>> Bar.NUM = 0  #=> ValueError: You can't change a constant

4

พจนานุกรม Python สามารถเปลี่ยนแปลงได้ดังนั้นพวกเขาจึงไม่ชอบวิธีที่ดีในการประกาศค่าคงที่:

>>> constants = {"foo":1, "bar":2}
>>> print constants
{'foo': 1, 'bar': 2}
>>> constants["bar"] = 3
>>> print constants
{'foo': 1, 'bar': 3}

4

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

เพียงกำหนดคลาสที่ว่างเปล่า

เช่น:

class RED: 
    pass
class BLUE: 
    pass

4

ในไพ ธ อนค่าคงที่เป็นเพียงตัวแปรที่มีชื่อในทุกเมืองหลวงโดยมีคำคั่นด้วยอักขระขีดล่าง

เช่น

DAYS_IN_WEEK = 7

ค่านั้นไม่แน่นอนเนื่องจากคุณสามารถเปลี่ยนค่าได้ แต่เมื่อกำหนดกฎสำหรับชื่อบอกว่าคุณคงที่ทำไมคุณจะ ฉันหมายความว่ามันเป็นโปรแกรมของคุณหลังจากทั้งหมด!

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

Python อาจเพิ่ม constคำหลัก ... แต่โปรแกรมเมอร์สามารถลบคำหลักแล้วเปลี่ยนค่าคงที่หากต้องการ แต่ทำไมถึงทำเช่นนั้น หากคุณต้องการทำลายกฎคุณสามารถเปลี่ยนกฎได้ แต่ทำไมตื๊อทำลายกฎถ้าชื่อทำให้ความตั้งใจชัดเจน?

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

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

นั่นคือปรัชญาของงูหลาม


4

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

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

def define(name, value):
  if (name + str(id(name))) not in globals():
    globals()[name + str(id(name))] = value

def constant(name):
  return globals()[name + str(id(name))]

define("PI",3.142)

print(constant("PI"))

ดูเหมือนว่ามันจะทำให้ PHP คงที่

ในความเป็นจริงแล้วทุกสิ่งที่ทุกคนต้องเปลี่ยนค่าคือ:

globals()["PI"+str(id("PI"))] = 3.1415

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

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


4

มีวิธีที่สะอาดกว่าในการทำเช่นนี้กับ namedtuple:

from collections import namedtuple


def make_consts(name, **kwargs):
    return namedtuple(name, kwargs.keys())(**kwargs)

ตัวอย่างการใช้งาน

CONSTS = make_consts("baz1",
                     foo=1,
                     bar=2)

ด้วยวิธีนี้คุณสามารถกำหนดค่าคงที่ของคุณ


สำหรับทุกคนที่อ่านข้อความนี้โปรดทราบว่าหากคุณตั้งค่าวัตถุที่ไม่แน่นอนเป็นหนึ่งในค่าคงที่เหล่านี้ทุกคนสามารถเปลี่ยนค่าภายในได้ ตัวอย่างเช่นให้ bar = [1, 2, 3] จากนั้นคุณสามารถทำได้ดังนี้: CONSTS.bar [1] = 'a' และมันจะไม่ถูกปฏิเสธ ดังนั้นควรระมัดระวังเกี่ยวกับเรื่องนี้
Juan Ignacio Sánchez

แทนที่จะเป็นวิธีแฮ็กที่ฉันทำเพื่อความสนุกฉันขอแนะนำให้ใช้เครื่องมือตกแต่งบ้านของ Python แทน
Juan Ignacio Sánchez

4

บางทีห้องสมุด pconst อาจช่วยคุณได้ ( github )

$ pip install pconst

from pconst import const
const.APPLE_PRICE = 100
const.APPLE_PRICE = 200

[Out] Constant value of "APPLE_PRICE" is not editable.


3

คุณสามารถใช้ StringVar หรือ IntVar ฯลฯ ค่าคงที่ของคุณคือconst_val

val = 'Stackoverflow'
const_val = StringVar(val)
const.trace('w', reverse)

def reverse(*args):
    const_val.set(val)

2

คุณสามารถทำได้ด้วยcollections.namedtupleและitertools:

import collections
import itertools
def Constants(Name, *Args, **Kwargs):
  t = collections.namedtuple(Name, itertools.chain(Args, Kwargs.keys()))
  return t(*itertools.chain(Args, Kwargs.values()))

>>> myConstants = Constants('MyConstants', 'One', 'Two', Three = 'Four')
>>> print myConstants.One
One
>>> print myConstants.Two
Two
>>> print myConstants.Three
Four
>>> myConstants.One = 'Two'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

2

(ย่อหน้านี้หมายถึงการแสดงความคิดเห็นในคำตอบเหล่านั้นที่นี่และที่นั่นซึ่งกล่าวถึงnamedtupleแต่ก็จะได้รับนานเกินไปที่จะพอดีกับการแสดงความคิดเห็นดังนั้นนี่มันไป.)

วิธีการที่ระบุชื่อข้างต้นเป็นนวัตกรรมที่แน่นอน เพื่อความสมบูรณ์แม้ว่าในตอนท้ายของส่วน NamedTuple ของเอกสารอย่างเป็นทางการก็อ่าน:

ค่าคงที่แจกแจงสามารถนำมาใช้กับ tuples ที่มีชื่อ แต่มันง่ายและมีประสิทธิภาพมากขึ้นในการใช้การประกาศคลาสอย่างง่าย:

class Status:
    open, pending, closed = range(3)

กล่าวอีกนัยหนึ่งประเภทเอกสารอย่างเป็นทางการของชอบที่จะใช้วิธีการปฏิบัติมากกว่าการใช้พฤติกรรมอ่านอย่างเดียว ฉันเดาว่ามันจะกลายเป็นอีกตัวอย่างหนึ่งของZen of Python :

เรียบง่ายดีกว่าซับซ้อน

การปฏิบัติจริงเต้นความบริสุทธิ์


2

นี่คือชุดของสำนวนที่ฉันสร้างขึ้นเพื่อพยายามปรับปรุงคำตอบที่มีอยู่แล้วบางส่วน

ฉันรู้ว่าการใช้ค่าคงที่ไม่ใช่ pythonic และคุณไม่ควรทำที่บ้าน!

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

โปรดอย่าเข้มงวดกับฉัน :-)

สำหรับรายละเอียดเพิ่มเติมผมเขียนบล็อกเกี่ยวกับการประกอบสำนวนเหล่านี้

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

ช่องว่างของค่าคงที่ (SpaceConstants)

สำนวนนี้สร้างสิ่งที่ดูเหมือนว่า namespace ของตัวแปรคงที่ (aka SpaceConstants) เป็นการดัดแปลงข้อมูลโค้ดโดยAlex Martelliเพื่อหลีกเลี่ยงการใช้วัตถุโมดูล โดยเฉพาะอย่างยิ่งการดัดแปลงนี้ใช้สิ่งที่ฉันเรียกโรงงานคลาสเพราะภายในฟังก์ชันSpaceConstantsเรียกว่าคลาส SpaceConstantsถูกกำหนดและอินสแตนซ์ของมันจะถูกส่งกลับ

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

def SpaceConstants():
    def setattr(self, name, value):
        if hasattr(self, name):
            raise AttributeError(
                "Cannot reassign members"
            )
        self.__dict__[name] = value
    cls = type('SpaceConstants', (), {
        '__setattr__': setattr
    })
    return cls()

sc = SpaceConstants()

print(sc.x) # raise "AttributeError: 'SpaceConstants' object has no attribute 'x'"
sc.x = 2 # bind attribute x
print(sc.x) # print "2"
sc.x = 3 # raise "AttributeError: Cannot reassign members"
sc.y = {'name': 'y', 'value': 2} # bind attribute y
print(sc.y) # print "{'name': 'y', 'value': 2}"
sc.y['name'] = 'yprime' # mutable object can be changed
print(sc.y) # print "{'name': 'yprime', 'value': 2}"
sc.y = {} # raise "AttributeError: Cannot reassign members"

ช่องว่างของค่าที่แช่แข็ง (SpaceFrozenValues)

สำนวนถัดไปนี้เป็นการดัดแปลงของSpaceConstantsที่ซึ่งวัตถุที่อ้างอิงได้ถูกแช่แข็ง การดำเนินการนี้จะใช้ประโยชน์จากสิ่งที่ผมเรียกใช้ร่วมกันปิดระหว่างsetattrและgetattrฟังก์ชั่น ค่าของวัตถุที่ไม่แน่นอนจะถูกคัดลอกและอ้างอิงโดยตัวแปรแคชกำหนดไว้ภายในฟังก์ชั่นการปิดที่ใช้ร่วมกัน มันเป็นสิ่งที่ฉันเรียกว่าสำเนาที่มีการป้องกันการปิดของวัตถุที่ไม่แน่นอนการป้องกันการปิดสำเนาของวัตถุที่ไม่แน่นอน

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

from copy import deepcopy

def SpaceFrozenValues():
    cache = {}
    def setattr(self, name, value):
        nonlocal cache
        if name in cache:
            raise AttributeError(
                "Cannot reassign members"
            )
        cache[name] = deepcopy(value)
    def getattr(self, name):
        nonlocal cache
        if name not in cache:
            raise AttributeError(
                "Object has no attribute '{}'".format(name)
            )
        return deepcopy(cache[name])
    cls = type('SpaceFrozenValues', (),{
        '__getattr__': getattr,
        '__setattr__': setattr
    })
    return cls()

fv = SpaceFrozenValues()
print(fv.x) # AttributeError: Object has no attribute 'x'
fv.x = 2 # bind attribute x
print(fv.x) # print "2"
fv.x = 3 # raise "AttributeError: Cannot reassign members"
fv.y = {'name': 'y', 'value': 2} # bind attribute y
print(fv.y) # print "{'name': 'y', 'value': 2}"
fv.y['name'] = 'yprime' # you can try to change mutable objects
print(fv.y) # print "{'name': 'y', 'value': 2}"
fv.y = {} # raise "AttributeError: Cannot reassign members"

พื้นที่คงที่ (ConstantSpace)

สำนวนนี้เป็น namespace ไม่เปลี่ยนรูปของตัวแปรคงที่หรือConstantSpace มันคือการรวมกันของคำตอบที่ง่าย awesomely จอนเบตต์ในStackOverflowกับโรงงานคลาส

def ConstantSpace(**args):
    args['__slots__'] = ()
    cls = type('ConstantSpace', (), args)
    return cls()

cs = ConstantSpace(
    x = 2,
    y = {'name': 'y', 'value': 2}
)

print(cs.x) # print "2"
cs.x = 3 # raise "AttributeError: 'ConstantSpace' object attribute 'x' is read-only"
print(cs.y) # print "{'name': 'y', 'value': 2}"
cs.y['name'] = 'yprime' # mutable object can be changed
print(cs.y) # print "{'name': 'yprime', 'value': 2}"
cs.y = {} # raise "AttributeError: 'ConstantSpace' object attribute 'x' is read-only"
cs.z = 3 # raise "AttributeError: 'ConstantSpace' object has no attribute 'z'"

พื้นที่แช่แข็ง (FrozenSpace)

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

from copy import deepcopy

def FreezeProperty(value):
    cache = deepcopy(value)
    return property(
        lambda self: deepcopy(cache)
    )

def FrozenSpace(**args):
    args = {k: FreezeProperty(v) for k, v in args.items()}
    args['__slots__'] = ()
    cls = type('FrozenSpace', (), args)
    return cls()

fs = FrozenSpace(
    x = 2,
    y = {'name': 'y', 'value': 2}
)

print(fs.x) # print "2"
fs.x = 3 # raise "AttributeError: 'FrozenSpace' object attribute 'x' is read-only"
print(fs.y) # print "{'name': 'y', 'value': 2}"
fs.y['name'] = 'yprime' # try to change mutable object
print(fs.y) # print "{'name': 'y', 'value': 2}"
fs.y = {} # raise "AttributeError: 'FrozenSpace' object attribute 'x' is read-only"
fs.z = 3 # raise "AttributeError: 'FrozenSpace' object has no attribute 'z'"

2

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

myVariable = 0
CONST_daysInWeek = 7    # This is a constant - do not change its value.   
CONSTANT_daysInMonth = 30 # This is also a constant - do not change this value.

หรือคุณอาจสร้างฟังก์ชั่นที่ทำหน้าที่เหมือนค่าคงที่:

def CONST_daysInWeek():
    return 7;

1

ในกรณีของฉันฉันต้องการ bytearrays ที่ไม่เปลี่ยนรูปแบบสำหรับการใช้งาน crypto library ที่มีตัวเลขตามตัวอักษรจำนวนมากที่ฉันต้องการเพื่อให้แน่ใจว่าคงที่

คำตอบนี้ใช้งานได้ แต่พยายามกำหนดองค์ประกอบ bytearray ใหม่ไม่ทำให้เกิดข้อผิดพลาด

def const(func):
    '''implement const decorator'''
    def fset(self, val):
        '''attempting to set a const raises `ConstError`'''
        class ConstError(TypeError):
            '''special exception for const reassignment'''
            pass

        raise ConstError

    def fget(self):
        '''get a const'''
        return func()

    return property(fget, fset)


class Consts(object):
    '''contain all constants'''

    @const
    def C1():
        '''reassignment to C1 fails silently'''
        return bytearray.fromhex('deadbeef')

    @const
    def pi():
        '''is immutable'''
        return 3.141592653589793

ค่าคงที่ไม่เปลี่ยนรูป แต่การกำหนดค่าคงที่แบบทดสอบไบต์ล้มเหลวอย่างเงียบ ๆ :

>>> c = Consts()
>>> c.pi = 6.283185307179586  # (https://en.wikipedia.org/wiki/Tau_(2%CF%80))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "consts.py", line 9, in fset
    raise ConstError
__main__.ConstError
>>> c.C1[0] = 0
>>> c.C1[0]
222
>>> c.C1
bytearray(b'\xde\xad\xbe\xef')

วิธีที่มีประสิทธิภาพง่ายขึ้นและอาจมากกว่า 'pythonic' ที่เกี่ยวข้องกับการใช้วัตถุ memoryview (วัตถุบัฟเฟอร์ใน <= python-2.6)

import sys

PY_VER = sys.version.split()[0].split('.')

if int(PY_VER[0]) == 2:
    if int(PY_VER[1]) < 6:
        raise NotImplementedError
    elif int(PY_VER[1]) == 6:
        memoryview = buffer

class ConstArray(object):
    '''represent a constant bytearray'''
    def __init__(self, init):
        '''
        create a hidden bytearray and expose a memoryview of that bytearray for
        read-only use
        '''
        if int(PY_VER[1]) == 6:
            self.__array = bytearray(init.decode('hex'))
        else:
            self.__array = bytearray.fromhex(init)

        self.array = memoryview(self.__array)

    def __str__(self):
        return str(self.__array)

    def __getitem__(self, *args, **kwargs):
       return self.array.__getitem__(*args, **kwargs)

การกำหนดรายการ ConstArray คือTypeError:

>>> C1 = ConstArray('deadbeef')
>>> C1[0] = 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'ConstArray' object does not support item assignment
>>> C1[0]
222

1

ฉันเขียน lib ใช้สำหรับหลาม const: kkconst - pypi สนับสนุน str, int, float, datetime

อินสแตนซ์ของฟิลด์ const จะยังคงทำงานประเภทฐาน

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

from __future__ import print_function
from kkconst import (
    BaseConst,
    ConstFloatField,
)

class MathConst(BaseConst):
    PI = ConstFloatField(3.1415926, verbose_name=u"Pi")
    E = ConstFloatField(2.7182818284, verbose_name=u"mathematical constant")  # Euler's number"
    GOLDEN_RATIO = ConstFloatField(0.6180339887, verbose_name=u"Golden Ratio")

magic_num = MathConst.GOLDEN_RATIO
assert isinstance(magic_num, ConstFloatField)
assert isinstance(magic_num, float)

print(magic_num)  # 0.6180339887
print(magic_num.verbose_name)  # Golden Ratio

รายละเอียดเพิ่มเติมการใช้งานที่คุณสามารถอ่าน urpi pypi: pypiหรือGitHub


1

คุณสามารถตัดค่าคงที่ในอาร์เรย์ numpy ตั้งค่าสถานะเป็นเขียนเท่านั้นและเรียกมันว่าโดยศูนย์ดัชนี

import numpy as np

# declare a constant
CONSTANT = 'hello'

# put constant in numpy and make read only
CONSTANT = np.array([CONSTANT])
CONSTANT.flags.writeable = False
# alternatively: CONSTANT.setflags(write=0)

# call our constant using 0 index    
print 'CONSTANT %s' % CONSTANT[0]

# attempt to modify our constant with try/except
new_value = 'goodbye'
try:
    CONSTANT[0] = new_value
except:
    print "cannot change CONSTANT to '%s' it's value '%s' is immutable" % (
        new_value, CONSTANT[0])

# attempt to modify our constant producing ValueError
CONSTANT[0] = new_value



>>>
CONSTANT hello
cannot change CONSTANT to 'goodbye' it's value 'hello' is immutable
Traceback (most recent call last):
  File "shuffle_test.py", line 15, in <module>
    CONSTANT[0] = new_value
ValueError: assignment destination is read-only

แน่นอนว่าสิ่งนี้จะปกป้องเนื้อหาของ numpy เท่านั้นไม่ใช่ตัวแปร "คงที่" นั่นเอง คุณยังสามารถทำได้:

CONSTANT = 'foo'

และCONSTANTจะเปลี่ยนอย่างไรก็ตามสิ่งนี้จะทำให้ TypeError เร็วในครั้งแรกที่CONSTANT[0]มีการเรียกใช้ในสคริปต์ในภายหลัง

แม้ว่า ... ฉันคิดว่าถ้าคุณมีบางจุดเปลี่ยนเป็น

CONSTANT = [1,2,3]

ตอนนี้คุณจะไม่ได้รับ TypeError อีกต่อไป hmmmm ....

https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.setflags.html

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