Làm cách nào để tạo một hằng trong Python?


991

Có cách nào để khai báo một hằng trong Python không? Trong Java, chúng ta có thể tạo các giá trị không đổi theo cách này:

public static final String CONST_NAME = "Name";

Tương đương với khai báo hằng số Java ở trên trong Python là gì?


6
trên thực tế, cách tạo các biến chỉ đọc là có thể thông qua chức năng / trang trí thuộc tính của python . các câu trả lời của inv là một ví dụ về việc sử dụng tùy chỉnh về điều đó. tuy nhiên, đặc tính này được sử dụng nhiều hơn thế, một phân tích tốt về cách thức hoạt động của nó trên các thuộc tính và phương thức Python của Shalabh Chaturvedi .
n611x007

20
IMHO, thi hành kiên định là "không pythonic". Trong Python 2.7 bạn thậm chí có thể viết True=Falsevà sau đó (2+2==4)==Truetrả về False.
osa

8
Như các câu trả lời khác cho thấy không có cách nào hoặc không cần phải khai báo hằng. Nhưng bạn có thể đọc PEP này về các quy ước. ví dụ: NIS_IS_A_CONSTANT
Rasika Perera

34
@osa: Bạn không thể làm điều đó trong python 3 - SyntaxError: can't assign to keyword. Điều này có vẻ như một điều tốt.
vô hiệu hóa

3
Ngạc nhiên điều này đã không được đề cập cho đến bây giờ, nhưng Enums có vẻ như là một cách tốt để xác định các hằng số liệt kê.
cs95

Câu trả lời:


973

Không có. Bạn không thể khai báo một biến hoặc giá trị là hằng số trong Python. Đừng thay đổi nó.

Nếu bạn đang ở trong một lớp, tương đương sẽ là:

class Foo(object):
    CONST_NAME = "Name"

nếu không, nó chỉ là

CONST_NAME = "Name"

Nhưng bạn có thể muốn xem đoạn mã liên tục trong Python của Alex Martelli.


Kể từ Python 3.8, có một typing.Finalchú thích biến sẽ cho biết các trình kiểm tra kiểu tĩnh (như mypy) rằng biến của bạn không nên được gán lại. Đây là tương đương gần nhất với Java final. Tuy nhiên, nó không thực sự ngăn chặn việc gán lại :

from typing import Final

a: Final = 1

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

27
Thay vào đó, hãy làm những gì trong "Hằng số trong Python", bạn nên sử dụng chức năng hoặc thuộc tính "thuộc tính".
Seth Johnson

26
Mọi người hỏi về tính năng tương tự trong Perl. Có một mô-đun nhập khẩu được gọi là "hằng số sử dụng", nhưng (AFAIK) chỉ là một trình bao bọc để tạo ra một hàm nhỏ trả về giá trị. Tôi làm tương tự trong Python. Ví dụ:def MY_CONST_VALUE(): return 123
kevinarpe

8
"Không có." Đúng, nhưng dựa trên công việc của người khác, tôi đã thêm một câu trả lời, bên dưới, với cách triển khai "Hằng" ngắn và đơn giản cho python 2.7 (thiếu "enum"). Đây là những chỉ đọc giống như enum name.attributevà có thể chứa bất kỳ giá trị nào. Tuyên bố rất dễ dàng Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0), Cách sử dụng rất đơn giản print 10 + Nums.PI, cố gắng thay đổi kết quả trong ngoại lệ Nums.PI = 22=> ValueError (..).
ToolmakerSteve 11/12/13

109
Đừng thay đổi nó. bạn đã thực hiện ngày của tôi
Hi-Angel

89
"Chỉ không thay đổi nó" không hữu ích chút nào. Nó không trả lời câu hỏi và tôi sẽ đề nghị rằng nó sẽ bị xóa.
Bartek Banachewicz

354

Không có consttừ khóa như trong các ngôn ngữ khác, tuy nhiên có thể tạo Thuộc tính có "hàm getter" để đọc dữ liệu, nhưng không có "hàm setter" để ghi lại dữ liệu. Điều này về cơ bản bảo vệ định danh khỏi bị thay đổi.

Đây là một triển khai thay thế bằng cách sử dụng thuộc tính lớp:

Lưu ý rằng mã không dễ dàng cho người đọc thắc mắc về hằng số. Xem giải thích bên dưới

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

Giải thích mã:

  1. Xác định hàm constantlấy một biểu thức và sử dụng nó để xây dựng một "getter" - một hàm chỉ trả về giá trị của biểu thức.
  2. Hàm setter tăng TypeError để nó chỉ đọc
  3. Sử dụng constantchức năng chúng ta vừa tạo như một trang trí để nhanh chóng xác định các thuộc tính chỉ đọc.

Và theo một cách khác lỗi thời hơn:

(Mã này khá phức tạp, giải thích thêm bên dưới)

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

Lưu ý rằng trang trí @apply dường như không được dùng nữa.

  1. Để xác định FOO định danh, Firs xác định hai hàm (fset, fget - tên theo lựa chọn của tôi).
  2. Sau đó sử dụng hàm dựng sẵn propertyđể xây dựng một đối tượng có thể là "set" hoặc "get".
  3. Lưu ý mũ propertyhai tham số đầu tiên của chức năng được đặt tên fsetfget.
  4. Sử dụng thực tế là chúng tôi đã chọn những tên này cho getter & setter của riêng chúng tôi và tạo một từ điển từ khóa bằng cách sử dụng ** (dấu sao đôi) được áp dụng cho tất cả các định nghĩa cục bộ của phạm vi đó để truyền tham số cho propertyhàm

11
Dựa trên tài liệu trên AttributeErrorTypeError, tôi nghĩ rằng ngoại lệ được nêu ra phải là một lỗi mới, mà tôi đề xuất đặt tên ConstantErrorhoặc một cái gì đó tương tự, đó là một lớp con của TypeError. Phần trong các tài liệu mà làm cho tôi nghĩ rằng: docs.python.org/2/library/exceptions.html
ArtOfWarfare

3
Tôi ngạc nhiên với mã này. Tại sao phông chữ phương thức FOO () và BAR () có tự đối số? IDE của tôi gạch chân các dấu ngoặc trên màu đỏ (lỗi "biên dịch"). Tôi mệt mỏi để đặt bản thân vào đó nhưng sau đó tôi gặp lỗi.
dùng3770060

10
Đi đến những độ dài này cho thấy sự thiếu hụt rõ ràng trong ngôn ngữ python. Tại sao họ không cảm thấy cần phải thêm cái này vào Python 3. Tôi không thể tin rằng không ai đề xuất nó và tôi chỉ đơn giản là không thể thấy logic đằng sau một số ủy ban đi 'nah, hằng số? không
Andrew S

8
Và giải pháp của bạn vẫn có thể được sửa đổi bởi một lập trình viên python xác định bằng cách sử dụngCONST.__dict__['FOO'] = 7
pppery

11
@OscarSmith, tôi nghĩ rằng nó sẽ cải thiện thiết kế 'mã tự ghi'. Khi tôi nói rõ về mã mà một số giá trị không thể thay đổi, sẽ dễ hiểu hơn là đọc tất cả mã nguồn và nhận ra rằng một số giá trị không bao giờ thay đổi. Ngoài ra, nó ngăn chặn khả năng ai đó thay đổi một giá trị nên, tốt, không đổi. Hãy nhớ rằng: rõ ràng là tốt hơn so với ngầm.
Gabriel

112

Trong Python thay vì ngôn ngữ thực thi một cái gì đó, mọi người sử dụng các quy ước đặt tên, ví dụ __methodcho các phương thức riêng tư và sử dụng _methodcho các phương thức được bảo vệ.

Vì vậy, theo cách tương tự, bạn có thể chỉ cần khai báo hằng là tất cả các mũ, ví dụ

MY_CONSTANT = "one"

Nếu bạn muốn hằng số này không bao giờ thay đổi, bạn có thể nối vào quyền truy cập thuộc tính và thực hiện các thủ thuật, nhưng cách tiếp cận đơn giản hơn là khai báo hàm

def MY_CONSTANT():
    return "one"

Vấn đề duy nhất là ở mọi nơi bạn sẽ phải thực hiện MY_CONSTANT (), nhưng một lần nữa MY_CONSTANT = "one"là cách chính xác trong python (thông thường).

Bạn cũng có thể sử dụng têntuple để tạo các hằng số:

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

18
Làm def MY_CONSTANT(): return "one"sẽ không dừng một ai đó, sau này trong mã, làm MY_CONSTANT = "two"(hoặc khai báo lại chức năng).
Matthew Schinckel

6
@MatthewSchinckel là về quy ước, đồng thời thay đổi MY_CONSTANT sẽ không thay đổi cách sử dụng MY_CONSTANT () nhưng sẽ gây ra lỗi và trong python nếu bạn muốn bạn có thể thay đổi bất cứ điều gì, không có mẹo thông minh nào có thể bảo vệ bạn.
Anurag Uniyal

3
Cảm ơn đã đưa ra cách tiếp cận têntuple. Chắc chắn đổi mới. Bạn cũng có thể tìm thấy "bình luận" của tôi ở đây có liên quan.
RayLuo

@MatthewSchinckel bạn có thể xác định lại BẤT CỨ LÚC NÀO trong trăn, vì vậy đó không thực sự là một điểm tốt.
cslotty

@MatthewSchinckel Ý tưởng không phải là viết MY_CONSTANT = MY_CONSTANT(), mà là sử dụng MY_CONSTANT()như hằng số. Tất nhiên, cái này. Nhưng điều này là tốt và rất phù hợp với nguyên tắc python "Chúng ta đều là người lớn ở đây" - tức là nhà phát triển sẽ hiếm khi bị cấm quyết định ghi đè lên một quy tắc khi họ có lý do chính đáng và biết họ đang làm gì.
jonathan.scholbach

69

Gần đây tôi đã tìm thấy một bản cập nhật rất ngắn gọn cho bản cập nhật này tự động đưa ra các thông báo lỗi có ý nghĩa và ngăn truy cập thông qua __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'

Chúng tôi xác định chính mình là biến bản thân thành một ví dụ và sau đó sử dụng các vị trí để đảm bảo rằng không có thuộc tính bổ sung nào có thể được thêm vào. Điều này cũng loại bỏ các __dict__tuyến truy cập. Tất nhiên, toàn bộ đối tượng vẫn có thể được xác định lại.

Chỉnh sửa - Giải pháp gốc

Có lẽ tôi đang thiếu một mẹo ở đây, nhưng điều này dường như có tác dụng với tôi:

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

Tạo cá thể cho phép __setattr__phương thức ma thuật khởi động và chặn các nỗ lực đặt FOObiến. Bạn có thể ném một ngoại lệ ở đây nếu bạn muốn. Khởi tạo thể hiện qua tên lớp sẽ ngăn truy cập trực tiếp qua lớp.

Đó là một nỗi đau cho một giá trị, nhưng bạn có thể gắn rất nhiều vào CONSTđối tượng của mình . Có một tầng lớp thượng lưu, tên lớp cũng có vẻ hơi kỳ cục, nhưng tôi nghĩ rằng nó khá ngắn gọn về tổng thể.


11
Đây là câu trả lời tốt nhất và rõ ràng nhất, bởi vì nó có ít "cơ chế" nhất, nhưng nhiều chức năng nhất. Tăng một ngoại lệ là quan trọng mặc dù ... không phải là một lựa chọn.
Erik Aronesty

Tôi đã tìm ra một tuyến đường ngắn hơn tự động tạo ra các lỗi có ý nghĩa nhưng nhiều theo cùng một kiểu. Tôi đã để lại ý tưởng ban đầu ở đây để so sánh.
Jon Betts

Thật đáng tiếc khi bạn vẫn cần CONST.tiền tố này . Ngoài ra trong các tình huống đa mô-đun, điều này sẽ phức tạp.
Alfe

1
Tôi nghĩ rằng thông thường bạn sẽ muốn nhóm các hằng số thành một số gói liên quan trong tình huống đó (chứ không phải có một đối tượng CONST khổng lồ), vì vậy nó có thể không phải là một điều tồi tệ.
Jon Betts

Tại sao câu trả lời này vẫn còn quá xa?! Các __slots__giải pháp rất thanh lịch và hiệu quả. Từ tất cả những gì tôi đã đọc, điều này gần giống như việc tạo các hằng số trong Python. Cảm ơn rât nhiều. Và đối với tất cả mọi người quan tâm, đây là một lời giải thích tuyệt vời và sâu sắc về __slots__phép thuật.
JohnGalt

34

Python không có hằng số.

Có lẽ cách thay thế dễ nhất là xác định hàm cho nó:

def MY_CONSTANT():
    return 42

MY_CONSTANT() bây giờ có tất cả các chức năng của một hằng số (cộng với một số niềng răng khó chịu).


1
Tôi chỉ muốn thêm đề xuất này nhưng may mắn thay, tôi đã cuộn xuống các câu trả lời được xếp hạng thấp. Tôi hy vọng nó sẽ được nâng cấp hơn nữa và tôi hoàn toàn đồng ý rằng nó có tất cả các chức năng của một hằng số và nó rất đơn giản và dễ hiểu. Nhìn vào số lượng mã soạn sẵn trong tất cả các giải pháp tinh vi, tôi thấy các dấu ngoặc nhọn tương đối khó tin.
yaccob

1
Đây là câu trả lời đơn giản nhất, mặc dù cần lưu ý rằng nó có một số chi phí và sẽ không dừng những kẻ ngốc sửa đổi giá trị trả về. Nó sẽ chỉ ngăn mã xuống dòng thay đổi nguồn
MrMesees

@MrMesees sửa đổi giá trị trả về? Bạn có nghĩa là chỉnh sửa nguồn? Nhưng từ điều này, bạn không được bảo vệ ngay cả trong C ++, trong đó các hằng số (như constexpr) là các hằng số cứng thực sự.
Ruslan

@Ruslan điều tôi muốn nói là vì python không có constexpr, nó sẽ không dừng giá trị được chỉnh sửa sau khi nó được đưa trở lại bối cảnh bên ngoài. Không có gì đã được thực hiện đến 42 để thực thi trạng thái đóng băng trong ví dụ này.
MrMesees

20

Ngoài hai câu trả lời hàng đầu (chỉ sử dụng các biến có tên UPPERCASE hoặc sử dụng các thuộc tính để làm cho các giá trị chỉ đọc), tôi muốn đề cập rằng có thể sử dụng siêu dữ liệu để triển khai các hằng số được đặt tên . Tôi cung cấp một giải pháp rất đơn giản bằng cách sử dụng siêu dữ liệu tại GitHub có thể hữu ích nếu bạn muốn các giá trị có nhiều thông tin hơn về loại / tên của chúng:

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

Đây là Python cao cấp hơn một chút, nhưng vẫn rất dễ sử dụng và tiện dụng. (Mô-đun có một số tính năng khác, bao gồm các hằng số chỉ đọc, xem README của nó.)

Có những giải pháp tương tự trôi nổi trong các kho khác nhau, nhưng theo hiểu biết của tôi, chúng thiếu một trong những tính năng cơ bản mà tôi mong đợi từ các hằng số (như là hằng số, hoặc là loại tùy ý), hoặc chúng có thêm các tính năng bí truyền làm cho chúng ít áp dụng chung. Nhưng YMMV, tôi sẽ biết ơn thông tin phản hồi. :-)


3
Tôi thích việc bạn thực hiện trên GitHub. Tôi gần như đã sẵn sàng để viết một lớp cơ bản thực hiện chức năng tra cứu ngược, nhưng tôi thấy bạn đã làm điều đó và hơn thế nữa!
Kerr

Cảm ơn, @Kerr, đó là phản hồi đầu tiên tôi nhận được và khiến tôi hài lòng. :-)
hans_meine

Tuyệt vời. Tôi chỉ thử điều này. Rất vui khi có tùy chọn này. Mặc dù vẫn chưa quyết định liệu tôi có quan tâm đúng mức đến khía cạnh chỉ đọc hay không, để sử dụng điều này thay vì chỉ đơn giản là làm def enum(**enums): return type('Enum', (), enums). Numbers = enum(ONE=1, TWO=2, THREE='three'), theo stackoverflow.com/a/1695250/199364 , phần "Trong các phiên bản trước ..."
ToolmakerSteve 10/12/13

19

Thuộc tính là một cách để tạo hằng. Bạn có thể làm điều đó bằng cách khai báo một thuộc tính getter, nhưng bỏ qua setter. Ví dụ:

class MyFinalProperty(object):

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

Bạn có thể xem một bài viết tôi đã viết để tìm thêm cách sử dụng các thuộc tính Python.


Theo giải pháp có giá trị. Tôi chỉ thực hiện điều này sau khi tìm thấy trang này (không phải câu trả lời này) và quay lại để thêm nó nếu chưa có. Tôi muốn nhấn mạnh sự hữu ích của câu trả lời này.
Marc

18

Chỉnh sửa: Đã thêm mã mẫu cho Python 3

Lưu ý: câu trả lời khác này có vẻ như nó cung cấp một triển khai hoàn chỉnh hơn nhiều tương tự như sau (với nhiều tính năng hơn).

Đầu tiên, tạo một siêu dữ liệu :

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

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

Điều này ngăn các thuộc tính thống kê được thay đổi. Sau đó tạo một lớp khác sử dụng siêu dữ liệu đó:

class Const(object):
    __metaclass__ = MetaConst

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

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

Hoặc, nếu bạn đang sử dụng Python 3:

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

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

Điều này sẽ ngăn các đạo cụ thể hiện bị thay đổi. Để sử dụng nó, kế thừa:

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

Bây giờ các đạo cụ, được truy cập trực tiếp hoặc thông qua một thể hiện, nên không đổi:

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

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

Đây là một ví dụ ở trên trong hành động. Đây là một ví dụ khác cho Python 3.


10

Bạn có thể sử dụng một tên được đặt làm giải pháp để tạo hiệu quả một hằng số hoạt động giống như một biến cuối cùng tĩnh trong Java (một "hằng số" Java). Khi giải pháp thay thế, đó là loại thanh lịch. (Một cách tiếp cận thanh lịch hơn sẽ chỉ đơn giản là cải thiện ngôn ngữ Python --- loại ngôn ngữ nào cho phép bạn xác định lại math.pi? - nhưng tôi lạc đề.)

(Khi tôi viết bài này, tôi nhận ra một câu trả lời khác cho câu hỏi này được đề cập có tên là tuple, nhưng tôi sẽ tiếp tục ở đây vì tôi sẽ hiển thị một cú pháp gần giống với những gì bạn mong đợi ở Java, vì không cần phải tạo tên như têntuple buộc bạn phải làm.)

Theo ví dụ của bạn, bạn sẽ nhớ rằng trong Java, chúng ta phải định nghĩa hằng số bên trong một số lớp ; bởi vì bạn đã không đề cập đến một tên lớp, hãy gọi nó Foo. Đây là lớp Java:

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

Đây là Python tương đương.

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

Điểm mấu chốt tôi muốn thêm ở đây là bạn không cần một Fooloại riêng (một "tuple tên nặc danh" sẽ rất hay, mặc dù nghe có vẻ như là một oxymoron), vì vậy chúng tôi đặt tên cho tên của chúng tôi _Foođể hy vọng nó sẽ không thoát để nhập mô-đun.

Điểm thứ hai ở đây là chúng ta ngay lập tức tạo một thể hiện của tên, gọi nó Foo; không cần phải làm điều này trong một bước riêng biệt (trừ khi bạn muốn). Bây giờ bạn có thể làm những gì bạn có thể làm trong Java:

>>> Foo.CONST_NAME
'Name'

Nhưng bạn không thể gán cho nó:

>>> Foo.CONST_NAME = 'bar'

AttributeError: can't set attribute

Lời cảm ơn: Tôi nghĩ rằng tôi đã phát minh ra cách tiếp cận có tên, nhưng sau đó tôi thấy rằng người khác đã đưa ra một câu trả lời tương tự (mặc dù ít gọn hơn). Sau đó, tôi cũng nhận thấy "các bộ dữ liệu được đặt tên" trong Python là gì? , chỉ ra rằng sys.version_infobây giờ là một tên được đặt tên, vì vậy có lẽ thư viện chuẩn Python đã nảy ra ý tưởng này sớm hơn nhiều.

Lưu ý rằng thật không may (đây vẫn là Python), bạn có thể xóa toàn bộ Foobài tập:

>>> Foo = 'bar'

(khẩu hiệu)

Nhưng ít nhất chúng ta đang ngăn chặn Foo.CONST_NAMEgiá trị bị thay đổi, và điều đó tốt hơn là không có gì. Chúc may mắn.


Cảm ơn đã đưa ra cách tiếp cận têntuple. Chắc chắn đổi mới. Bạn cũng có thể tìm thấy "bình luận" của tôi ở đây có liên quan.
RayLuo

10

PEP 591 có vòng loại 'cuối cùng'. Thực thi là xuống loại kiểm tra.

Vì vậy, bạn có thể làm:

MY_CONSTANT: Final = 12407

Ghi chú: Final từ khóa chỉ áp dụng cho phiên bản Python 3.8


9

Đây là một triển khai của lớp "Hằng", tạo ra các thể hiện với các thuộc tính chỉ đọc (không đổi). Ví dụ: có thể sử dụng Nums.PIđể có được một giá trị đã được khởi tạo là 3.14159Nums.PI = 22đưa ra một ngoại lệ.

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

Cảm ơn FrozenDict của @MikeGraham , mà tôi đã sử dụng làm điểm khởi đầu. Thay đổi, vì vậy thay vì Nums['ONE']cú pháp sử dụng là Nums.ONE.

Và cảm ơn câu trả lời của @ Raufio, vì ý tưởng ghi đè __ setattr __.

Hoặc để triển khai với nhiều chức năng hơn, hãy xem tên_constants của @Hans_meine tại GitHub


2
Python là ngôn ngữ của người lớn đồng ý. Không có sự bảo vệ chống lại một cái gì đó như thế này. Nums._d['PI'] = 22 Bản thân ngôn ngữ không cung cấp bất kỳ cách nào để đánh dấu mọi thứ là không thể thay đổi, tôi tin.
Ajay M

8

Một tuple về mặt kỹ thuật đủ điều kiện là một hằng số, vì một tuple sẽ phát sinh lỗi nếu bạn cố gắng thay đổi một trong các giá trị của nó. Nếu bạn muốn khai báo một tuple với một giá trị, sau đó đặt dấu phẩy sau giá trị duy nhất của nó, như sau:

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

Để kiểm tra giá trị của biến này, hãy sử dụng giá trị tương tự như sau:

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

Nếu bạn cố gắng thay đổi giá trị này, một lỗi sẽ xuất hiện.


7

Tôi sẽ tạo một lớp ghi đè __setattr__phương thức của lớp đối tượng cơ sở và bọc các hằng số của tôi với điều đó, lưu ý rằng tôi đang sử dụng 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)

Để quấn một chuỗi:

>>> 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'

Điều này khá đơn giản, nhưng nếu bạn muốn sử dụng các hằng số của mình giống như một đối tượng không cố định (không sử dụng constObj.value), nó sẽ chuyên sâu hơn một chút. Có thể điều này có thể gây ra sự cố, vì vậy tốt nhất là giữ .valuecho chương trình hiển thị và biết rằng bạn đang thực hiện các thao tác với hằng số (mặc dù có thể không phải là cách 'pythonic' nhất).


+1 cho cách tiếp cận thú vị. Mặc dù không sạch sẽ như câu trả lời đã được cung cấp. Và ngay cả giải pháp def ONE(): return 1được đề xuất đơn giản trước đó cũng dễ sử dụng ONE()hơn câu trả lời này ONE.value.
ToolmakerSteve 10/12/13

7

Thật không may, Python chưa có hằng số và thật xấu hổ. ES6 đã thêm các hằng hỗ trợ vào JavaScript ( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/const ) vì đây là một điều rất hữu ích trong bất kỳ ngôn ngữ lập trình nào. Như đã trả lời trong các câu trả lời khác trong cộng đồng Python, hãy sử dụng quy ước - biến chữ hoa của người dùng làm hằng số, nhưng nó không bảo vệ chống lại các lỗi tùy ý trong mã. Nếu bạn thích, bạn có thể thấy một giải pháp tập tin hữu ích như tiếp theo (xem tài liệu về cách sử dụng nó).

tập tin 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()

Nếu điều này là không đủ, xem toàn bộ testcase cho nó.

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"})

Ưu điểm: 1. Truy cập vào tất cả các hằng số cho toàn bộ dự án 2. Kiểm soát chặt chẽ các giá trị của hằng số

Thiếu: 1. Không hỗ trợ cho các loại tùy chỉnh và loại 'dict'

Ghi chú:

  1. Đã thử nghiệm với Python3.4 và Python3.5 (Tôi đang sử dụng 'độc tố' cho nó)

  2. Môi trường thử nghiệm:

.

$ 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

Bạn có thể cải thiện điều này một chút bằng cách tự động chuyển đổi từ điển sang các bộ dữ liệu được đặt tên
Peter Schorn

6

Cách khai báo "hằng số" của Pythonic về cơ bản là một biến cấp mô-đun:

RED = 1
GREEN = 2
BLUE = 3

Và sau đó viết các lớp hoặc chức năng của bạn. Vì các hằng số hầu như luôn luôn là số nguyên và chúng cũng là bất biến trong Python, nên bạn có rất ít cơ hội để thay đổi nó.

Trừ khi, tất nhiên, nếu bạn thiết lập rõ ràng RED = 2.


21
Có, nhưng chặn khả năng "thiết lập rõ ràng RED = 2" là toàn bộ lợi ích (trong các ngôn ngữ khác) của việc có thể khai báo một tên biến là "không đổi"!
ToolmakerSteve 10/12/13

6
Bạn sẽ có được lợi ích gì từ việc ngăn chặn điều đó? Điều hữu ích nhất về const thường là tối ưu hóa trình biên dịch không thực sự là một thứ trong Python. Muốn một cái gì đó không đổi? Đừng thay đổi nó. Nếu bạn lo lắng về việc người khác thay đổi nó, bạn có thể đặt nó bên ngoài phạm vi của họ hoặc chỉ nhận ra rằng, nếu ai đó đang thay đổi, đó là vấn đề của họ và họ cần phải giải quyết chứ không phải bạn.
Kevin

@Kevin: " Bạn sẽ nhận được lợi ích gì ... ", lợi ích của staticviệc có một bộ lưu trữ duy nhất cho giá trị cho tất cả các phiên bản của một lớp? Trừ khi có khả năng khai báo một biến tĩnh / lớp thực sự.
phút

8
Vấn đề gốc là một số người có thể coi đó là giá trị là nguồn của sự thật, không thể thay đổi và sử dụng nó làm nguồn sự thật trong suốt mã của họ thay vì giới thiệu các giá trị ma thuật (mà tôi thấy rất nhiều trong Python) - và những người khác có thể xem nó như một cái gì đó họ được phép thay đổi theo ý muốn. Khi ai đó thay đổi biến toàn cục và bạn không thể biết được nó đã thay đổi ở đâu và ứng dụng gặp sự cố vì RED = "xanh" thay vì "đỏ", bạn đang đưa ra một vấn đề hoàn toàn không cần thiết đã được giải quyết một cách đơn giản và được hiểu toàn cầu.
Dagroom

5

Chúng ta có thể tạo một đối tượng mô tả.

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) Nếu chúng tôi muốn làm việc với các hằng số ở cấp độ cá thể thì:

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) nếu chúng ta chỉ muốn tạo các hằng số ở cấp độ lớp, chúng ta có thể sử dụng một siêu dữ liệu dùng làm vật chứa cho các hằng số (các đối tượng mô tả của chúng ta); tất cả các lớp hạ xuống sẽ kế thừa các hằng số của chúng ta (các đối tượng mô tả của chúng ta) mà không có bất kỳ rủi ro nào có thể được sửa đổi.

# 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

Nếu tôi tạo một lớp con của Foo, lớp này sẽ kế thừa hằng số mà không có khả năng sửa đổi chúng

class Bar(Foo): pass

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

4

Từ điển Python có thể thay đổi, vì vậy chúng dường như không phải là một cách hay để khai báo hằng:

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

4

Đây là một mẹo nếu bạn muốn hằng số và không quan tâm đến giá trị của chúng:

Chỉ cần định nghĩa các lớp trống.

ví dụ:

class RED: 
    pass
class BLUE: 
    pass

4

Trong python, hằng số chỉ đơn giản là một biến có tên trong tất cả các chữ viết hoa, với các từ được phân tách bằng ký tự gạch dưới,

ví dụ

DAYS_IN_WEEK = 7

Giá trị là có thể thay đổi, vì trong bạn có thể thay đổi nó. Nhưng đưa ra các quy tắc cho tên cho bạn là một hằng số, tại sao bạn? Ý tôi là, đó là chương trình của bạn sau tất cả!

Đây là cách tiếp cận được thực hiện trong suốt con trăn. Không có privatetừ khóa cho cùng một lý do. Tiền tố tên với một dấu gạch dưới và bạn biết nó được dự định là riêng tư. Mã có thể phá vỡ quy tắc .... giống như một lập trình viên có thể loại bỏ từ khóa riêng nào.

Python có thể đã thêm một const từ khóa ... nhưng một lập trình viên có thể xóa từ khóa và sau đó thay đổi hằng số nếu họ muốn, nhưng tại sao lại làm vậy? Nếu bạn muốn phá vỡ quy tắc, bạn vẫn có thể thay đổi quy tắc. Nhưng tại sao phải phá vỡ quy tắc nếu tên làm cho ý định rõ ràng?

Có lẽ có một số thử nghiệm đơn vị trong đó có ý nghĩa để áp dụng thay đổi giá trị? Để xem những gì xảy ra trong một tuần 8 ngày mặc dù trong thế giới thực, số ngày trong tuần không thể thay đổi. Nếu ngôn ngữ ngăn bạn tạo một ngoại lệ nếu chỉ có một trường hợp này bạn cần phá vỡ quy tắc ... thì bạn sẽ phải ngừng khai báo nó là một hằng số, mặc dù nó vẫn là một hằng số trong ứng dụng, và có chỉ một trường hợp thử nghiệm này sẽ thấy điều gì xảy ra nếu nó được thay đổi.

Tất cả tên viết hoa cho bạn biết nó được dự định là một hằng số. Đó là điều quan trọng. Không phải là một ngôn ngữ buộc các ràng buộc về mã, bạn có quyền thay đổi.

Đó là triết lý của trăn.


4

Không có cách hoàn hảo để làm điều này. Theo tôi hiểu, hầu hết các lập trình viên sẽ chỉ viết hoa định danh, vì vậy PI = 3.142 có thể dễ dàng được hiểu là một hằng số.

Mặt khác, nếu bạn muốn một cái gì đó thực sự hoạt động như một hằng số, tôi không chắc bạn sẽ tìm thấy nó. Với bất cứ điều gì bạn làm, sẽ luôn có một số cách chỉnh sửa "hằng số" để nó thực sự không phải là một hằng số. Đây là một ví dụ rất đơn giản, bẩn thỉu:

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

Điều này có vẻ như nó sẽ làm cho một kiểu PHP không đổi.

Trong thực tế, tất cả những gì người ta cần để thay đổi giá trị là thế này:

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

Điều này cũng tương tự đối với tất cả các giải pháp khác mà bạn sẽ tìm thấy ở đây - ngay cả những giải pháp thông minh tạo ra một lớp và xác định lại phương thức thuộc tính đã đặt - sẽ luôn có cách xung quanh chúng. Đó chỉ là cách Python.

Đề nghị của tôi là chỉ cần tránh tất cả các rắc rối và chỉ cần viết hoa định danh của bạn. Nó sẽ không thực sự là một hằng số thích hợp nhưng sau đó một lần nữa sẽ không có gì.


4

Có một cách sạch hơn để làm điều này với nametuple:

from collections import namedtuple


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

Ví dụ sử dụng

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

Với cách tiếp cận chính xác này, bạn có thể đặt tên cho hằng số của mình.


Đối với tất cả những ai đang đọc điều này, xin vui lòng, hãy nhớ rằng, nếu bạn đặt một đối tượng có thể thay đổi là một trong những hằng số này, bất kỳ ai cũng có thể thay đổi giá trị bên trong của nó. ví dụ: let bar = [1, 2, 3], sau đó, bạn có thể làm như sau: CONSTS.bar [1] = 'a' và nó sẽ không bị từ chối. Vì vậy, hãy cẩn thận về điều này.
Juan Ignacio Sánchez

Thay vì phương pháp hacky mà tôi tạo ra chỉ để giải trí, tôi khuyên bạn nên sử dụng trang trí thuộc tính của Python thay thế.
Juan Ignacio Sánchez

4

Có lẽ thư viện pconst sẽ giúp bạn ( 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

Bạn có thể sử dụng StringVar hoặc IntVar, v.v., hằng số của bạn là const_val

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

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

2

Bạn có thể làm điều đó với collections.namedtupleitertools:

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

(Đoạn này có nghĩa là một nhận xét về những câu trả lời ở đâyở đó , đã đề cập namedtuple, nhưng nó đang trở nên quá dài để phù hợp với một nhận xét, vì vậy, nó ở đây.)

Cách tiếp cận có tên được đề cập ở trên chắc chắn là sáng tạo. Tuy nhiên, để hoàn thiện, ở phần cuối của NamedTuple trong tài liệu chính thức , nó ghi:

Các hằng số liệt kê có thể được thực hiện với các bộ dữ liệu có tên, nhưng sử dụng khai báo lớp đơn giản và hiệu quả hơn:

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

Nói cách khác, loại tài liệu chính thức thích sử dụng một cách thực tế, hơn là thực sự thực hiện hành vi chỉ đọc. Tôi đoán nó trở thành một ví dụ khác về Zen của Python :

Đơn giản là tốt hơn phức tạp.

tính thực tế nhịp đập tinh khiết.


2

Đây là một tập hợp các thành ngữ mà tôi đã tạo ra như một nỗ lực để cải thiện một số câu trả lời đã có sẵn.

Tôi biết việc sử dụng hằng số không phải là pythonic, và bạn không nên làm điều này ở nhà!

Tuy nhiên, Python là một ngôn ngữ năng động như vậy! Diễn đàn này cho thấy làm thế nào có thể tạo ra các cấu trúc trông và cảm giác như hằng số. Câu trả lời này có mục đích chính là khám phá những gì có thể diễn đạt bằng ngôn ngữ.

Xin đừng quá gay gắt với tôi :-).

Để biết thêm chi tiết tôi đã viết một blog đệm về những thành ngữ này .

Trong bài viết này, tôi sẽ gọi một biến không đổi đến một tham chiếu không đổi đến các giá trị (không thay đổi hoặc khác). Hơn nữa, tôi nói rằng một biến có giá trị đóng băng khi tham chiếu một đối tượng có thể thay đổi mà mã máy khách không thể cập nhật (các) giá trị của nó.

Một không gian của hằng số (SpaceConstants)

Thành ngữ này tạo ra cái trông giống như một không gian tên của các biến không đổi (hay còn gọi là SpaceConstants). Đây là bản sửa đổi đoạn mã của Alex Martelli để tránh việc sử dụng các đối tượng mô-đun. Cụ thể, sửa đổi này sử dụng cái mà tôi gọi là một nhà máy lớp vì trong hàm SpaceConstants , một lớp có tên là SpaceConstants được định nghĩa và một thể hiện của nó được trả về.

Tôi đã khám phá việc sử dụng lớp của nhà máy để triển khai một thiết kế giống như chính sách trong Python trong stackoverflow và cả trong một blogpost .

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"

Một không gian của các giá trị bị đóng băng (SpaceF FrozenValues)

Thành ngữ tiếp theo này là một sửa đổi của SpaceConstants trong đó các đối tượng có thể thay đổi được tham chiếu được đóng băng. Việc triển khai này khai thác cái mà tôi gọi là đóng chia sẻ giữa các hàm setattrgetattr . Giá trị của đối tượng có thể thay đổi được sao chép và được tham chiếu bởi bộ đệm biến đổi xác định bên trong hàm đóng chia sẻ hàm. Nó tạo thành cái mà tôi gọi là bản sao được bảo vệ của một đối tượng có thể thay đổi .

Bạn phải cẩn thận khi sử dụng thành ngữ này vì getattr trả về giá trị của bộ đệm bằng cách thực hiện một bản sao sâu. Hoạt động này có thể có một tác động hiệu suất đáng kể trên các đối tượng lớn!

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"

Một không gian không đổi (ConstantSpace)

Thành ngữ này là một không gian tên bất biến của các biến không đổi hoặc ConstantSpace . Nó là sự kết hợp giữa câu trả lời cực kỳ đơn giản của Jon Betts trong stackoverflow với một nhà máy sản xuất lớp .

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'"

Một không gian đóng băng (FrozenSpace)

Thành ngữ này là một không gian tên bất biến của các biến đóng băng hoặc FrozenSpace . Nó được lấy từ mẫu trước bằng cách biến mỗi biến thành một thuộc tính được bảo vệ bằng cách đóng lớp FrozenSpace được tạo .

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

Trong Python, các hằng số không tồn tại, nhưng bạn có thể chỉ ra rằng một biến là một hằng số và không được thay đổi bằng cách thêm CONST_vào đầu tên biến và nói rằng đó là một hằng số trong một nhận xét:

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.

Ngoài ra, bạn có thể tạo một hàm hoạt động như một hằng số:

def CONST_daysInWeek():
    return 7;

1

Trong trường hợp của tôi, tôi cần các khoản phụ không thay đổi để triển khai thư viện tiền điện tử chứa nhiều số bằng chữ mà tôi muốn đảm bảo là không đổi.

Câu trả lời này hoạt động nhưng đã cố gắng gán lại các phần tử bytearray không gây ra lỗi.

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ác hằng số là bất biến, nhưng việc gán bytearray liên tục thất bại trong âm thầm:

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

Một cách tiếp cận mạnh mẽ hơn, đơn giản hơn và thậm chí có thể là 'pythonic' hơn liên quan đến việc sử dụng các đối tượng memoryview (đối tượng đệm trong <= 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)

Việc gán vật phẩm ConstArray là 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

Tôi viết một lib lib cho python const: kkconst - pypi hỗ trợ str, int, float, datetime

trường hợp const sẽ giữ hành vi loại cơ sở của nó.

Ví dụ:

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

biết thêm chi tiết cách sử dụng, bạn có thể đọc url pypi : pypi hoặc github


1

Bạn có thể bọc một hằng số trong một mảng numpy, chỉ đánh dấu nó viết và luôn gọi nó bằng chỉ số 0.

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

tất nhiên điều này chỉ bảo vệ nội dung của numpy, chứ không phải biến "CONSTANT"; bạn vẫn có thể làm:

CONSTANT = 'foo'

CONSTANT sẽ thay đổi, tuy nhiên, điều đó sẽ nhanh chóng ném TypeError lần đầu tiên CONSTANT[0]sau đó được gọi trong tập lệnh.

mặc dù ... tôi cho rằng nếu một lúc nào đó bạn đổi nó thành

CONSTANT = [1,2,3]

bây giờ bạn sẽ không nhận được TypeError nữa. hmmmm ....

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.