Bagaimana cara membuat konstanta dengan Python?

991

Apakah ada cara untuk mendeklarasikan konstanta dengan Python? Di Jawa kita bisa membuat nilai konstan dengan cara ini:

public static final String CONST_NAME = "Name";

Apa yang setara dengan deklarasi konstan Java di atas dalam Python?

zfranciscus
sumber
6
sebenarnya cara membuat variabel read-only dimungkinkan melalui fungsi / dekorator properti python . yang jawabannya dari inv adalah contoh dari penggunaan kebiasaan itu. properti lebih umum digunakan daripada itu, meskipun, analisis yang baik tentang cara kerjanya adalah pada Atribut dan Metode Python Shalabh Chaturvedi .
n611x007
20
IMHO, menegakkan keteguhan adalah "bukan pythonic". Dalam Python 2.7 Anda bahkan dapat menulis True=False, dan kemudian (2+2==4)==Truekembali False.
Osa
8
Seperti jawaban lain menyarankan tidak ada cara atau tidak perlu mendeklarasikan konstanta. Tetapi Anda dapat membaca PEP ini tentang konvensi. mis. THIS_IS_A_CONSTANT
Rasika Perera
34
@ Esa: Anda tidak bisa melakukan itu di python 3 - SyntaxError: can't assign to keyword. Ini sepertinya Good Thing.
nucky101
3
Terkejut ini belum disebutkan sampai sekarang, tetapi Enums akan tampak seperti cara yang baik untuk mendefinisikan konstanta yang disebutkan.
cs95

Jawaban:

973

Tidak, tidak ada. Anda tidak bisa mendeklarasikan variabel atau nilai sebagai konstan dalam Python. Hanya saja, jangan mengubahnya.

Jika Anda berada di kelas, yang sederajat adalah:

class Foo(object):
    CONST_NAME = "Name"

jika tidak, itu adil

CONST_NAME = "Name"

Tetapi Anda mungkin ingin melihat pada potongan kode Konstanta dalam Python oleh Alex Martelli.


Pada Python 3.8, ada typing.Finalanotasi variabel yang akan memberi tahu pemeriksa tipe statis (seperti mypy) bahwa variabel Anda tidak boleh dipindahkan. Ini setara dengan Java final. Namun, itu sebenarnya tidak mencegah penugasan kembali :

from typing import Final

a: Final = 1

# Executes fine, but mypy will report an error if you run mypy on this:
a = 2
Felix Kling
sumber
27
Daripada melakukan apa yang ada di "Konstanta dengan Python," Anda harus menggunakan fungsi atau dekorator "properti".
Seth Johnson
26
Orang-orang bertanya tentang fitur yang sama di Perl. Ada modul impor yang disebut "gunakan konstan", tetapi (AFAIK) apakah itu hanya pembungkus untuk membuat fungsi kecil yang mengembalikan nilai. Saya melakukan hal yang sama dengan Python. Contoh:def MY_CONST_VALUE(): return 123
kevinarpe
8
"Tidak, tidak ada." Benar, tetapi membangun atas karya orang lain, saya telah menambahkan jawaban, jauh di bawah, dengan implementasi "Konstanta" pendek dan sederhana untuk python 2.7 (yang tidak memiliki "enum"). Ini adalah read-only seperti enum name.attribute, dan dapat berisi nilai apa pun. Deklarasi mudah Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0), Penggunaan mudah print 10 + Nums.PI, berusaha mengubah hasil dalam pengecualian Nums.PI = 22=> ValueError (..).
ToolmakerSteve
110
Hanya saja, jangan mengubahnya. Anda membuat hari saya
Hi-Angel
89
"Jangan mengubahnya" tidak membantu sama sekali. Itu tidak menjawab pertanyaan dan saya akan menyarankan itu dihapus.
Bartek Banachewicz
354

Tidak ada constkata kunci seperti dalam bahasa lain, namun dimungkinkan untuk membuat Properti yang memiliki "fungsi pengambil" untuk membaca data, tetapi tidak ada "fungsi setter" untuk menulis ulang data. Ini pada dasarnya melindungi pengidentifikasi dari perubahan.

Berikut ini adalah implementasi alternatif menggunakan properti kelas:

Perhatikan bahwa kode ini jauh dari mudah bagi pembaca yang bertanya-tanya tentang konstanta. Lihat penjelasan di bawah ini

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

Penjelasan Kode:

  1. Tentukan fungsi constantyang mengambil ekspresi, dan gunakan untuk membangun "pengambil" - fungsi yang hanya mengembalikan nilai ekspresi.
  2. Fungsi setter memunculkan TypeError sehingga hanya-baca
  3. Gunakan constantfungsi yang baru saja kita buat sebagai dekorasi untuk dengan cepat mendefinisikan properti hanya baca.

Dan dengan cara lain yang lebih kuno:

(Kode ini cukup rumit, penjelasan lebih lanjut di bawah)

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

Perhatikan bahwa dekorator @apply tampaknya sudah usang.

  1. Untuk mendefinisikan FOO pengidentifikasi, pertama mendefinisikan dua fungsi (fset, fget - namanya ada pada pilihan saya).
  2. Kemudian gunakan propertyfungsi bawaan untuk membangun objek yang dapat "diatur" atau "dapatkan".
  3. Perhatikan bahwa propertydua parameter pertama fungsi diberi nama fsetdan fget.
  4. Gunakan fakta bahwa kami memilih nama-nama ini untuk pengambil & penyetel kami sendiri dan membuat kamus kata kunci menggunakan ** (tanda bintang ganda) yang diterapkan pada semua definisi lokal lingkup itu untuk meneruskan parameter ke propertyfungsi
inv
sumber
11
Berdasarkan dokumentasi pada AttributeErrordan TypeError, saya berpikir bahwa pengecualian yang diajukan harus merupakan kesalahan baru, yang saya usulkan penamaan ConstantErroratau sesuatu seperti itu, yang merupakan subkelas dari TypeError. Bagian dalam dokumen yang membuat saya berpikir bahwa: docs.python.org/2/library/exceptions.html
ArtOfWarfare
3
Saya terkejut dengan kode ini. Mengapa font metode FOO () dan BAR () memiliki argumen sendiri? IDE saya menggarisbawahi tanda kurung pada kesalahan merah ("kompilasi"). Saya lelah menempatkan diri di dalamnya tetapi kemudian saya mendapatkan kesalahan.
user3770060
10
Pergi ke panjang ini tidak menguraikan kekurangan yang jelas dalam bahasa python. Mengapa mereka tidak merasa perlu menambahkan ini ke Python 3. Saya tidak percaya tidak ada yang menyarankan itu dan saya tidak bisa melihat logika di balik beberapa komite yang akan 'nah, konstanta? nah. '
Andrew S
8
Dan solusi Anda masih dapat dimodifikasi oleh programmer python yang ditentukan dengan menggunakanCONST.__dict__['FOO'] = 7
pppery
11
@ OscarSmith, saya pikir ini akan meningkatkan desain 'self documented code'. Ketika saya membuat eksplisit ke dalam kode bahwa beberapa nilai tidak dapat berubah, lebih mudah untuk dipahami daripada membaca semua kode sumber dan menyadari bahwa beberapa nilai tidak pernah berubah. Juga, itu memblokir kemungkinan seseorang mengubah nilai yang seharusnya, yah, konstan. Ingat: eksplisit lebih baik daripada implisit.
Gabriel
112

Dalam Python alih-alih bahasa menegakkan sesuatu, orang menggunakan konvensi penamaan misalnya __methoduntuk metode pribadi dan menggunakan _methoduntuk metode yang dilindungi.

Jadi dengan cara yang sama Anda cukup mendeklarasikan konstanta seperti semua kapitalisasi misalnya

MY_CONSTANT = "one"

Jika Anda ingin agar konstanta ini tidak pernah berubah, Anda dapat menghubungkan ke akses atribut dan melakukan trik, tetapi pendekatan yang lebih sederhana adalah dengan mendeklarasikan suatu fungsi

def MY_CONSTANT():
    return "one"

Satu-satunya masalah adalah di mana-mana Anda harus melakukan MY_CONSTANT (), tetapi sekali lagi MY_CONSTANT = "one"adalah cara yang benar dalam python (biasanya).

Anda juga dapat menggunakan namedtuple untuk membuat konstanta:

>>> 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
Anurag Uniyal
sumber
18
Melakukan def MY_CONSTANT(): return "one"tidak akan menghentikan seseorang, nanti dalam kode, melakukan MY_CONSTANT = "two"(atau mendeklarasikan ulang fungsi).
Matthew Schinckel
6
@MatthewSchinckel ini tentang konvensi, juga mengubah MY_CONSTANT tidak akan mengubah penggunaan MY_CONSTANT () tetapi akan membuang kesalahan, dan dengan python jika Anda mau, Anda dapat mengubah apa pun, tidak ada trik pintar yang dapat melindungi Anda.
Anurag Uniyal
3
Terima kasih telah mengemukakan pendekatan namesuple. Sangat inovatif. Anda mungkin juga menemukan "komentar" saya di sini relevan.
RayLuo
@ MatthewSchinckel Anda dapat mendefinisikan kembali APA SAJA dengan python, jadi itu bukan poin yang bagus.
cslotty
@ MatthewSchinckel Idenya bukan untuk menulis MY_CONSTANT = MY_CONSTANT(), tetapi untuk digunakan MY_CONSTANT()sebagai konstanta. Tentu saja ini. Tapi ini baik-baik saja dan sangat sejalan dengan prinsip python "Kita semua orang dewasa di sini" - yaitu pengembang jarang akan dilarang untuk memutuskan untuk menimpa aturan ketika mereka memiliki alasan yang baik dan tahu apa yang mereka lakukan.
jonathan.scholbach
69

Baru-baru ini saya menemukan pembaruan yang sangat ringkas untuk ini yang secara otomatis memunculkan pesan kesalahan yang bermakna dan mencegah akses melalui __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'

Kami mendefinisikan diri kami sendiri sebagai contoh dan kemudian menggunakan slot untuk memastikan bahwa tidak ada atribut tambahan yang dapat ditambahkan. Ini juga menghapus __dict__rute akses. Tentu saja, keseluruhan objek masih dapat didefinisikan ulang.

Edit - Solusi asli

Saya mungkin melewatkan trik di sini, tetapi ini sepertinya berhasil bagi saya:

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

Menciptakan instance memungkinkan __setattr__metode ajaib untuk menendang dan mencegat upaya untuk mengatur FOOvariabel. Anda bisa melempar pengecualian di sini jika Anda mau. Instantiasi instance atas nama kelas mencegah akses langsung melalui kelas.

Benar-benar menyakitkan untuk satu nilai, tetapi Anda bisa menempelkan banyak pada CONSTobjek Anda . Memiliki kelas atas, nama kelasnya juga tampak agak kasar, tapi saya pikir secara keseluruhan cukup ringkas.

Jon Betts
sumber
11
Ini adalah jawaban terbaik dan paling jelas, karena memiliki "mekanisme" paling sedikit, tetapi paling fungsional. Memunculkan pengecualian itu penting ... bukan pilihan.
Erik Aronesty
Saya telah mengerjakan rute yang lebih pendek yang secara otomatis menghasilkan kesalahan yang berarti tetapi jauh dengan gaya yang sama. Saya telah meninggalkan ide asli di sini untuk perbandingan.
Jon Betts
Sayang sekali bahwa Anda masih membutuhkan CONST.awalan ini . Juga dalam situasi multi-modul ini akan menjadi rumit.
Alfe
1
Saya pikir biasanya Anda ingin mengelompokkan konstanta ke dalam beberapa bundel terkait pula dalam situasi itu (daripada memiliki satu objek CONST raksasa), jadi itu mungkin bukan hal yang buruk.
Jon Betts
Mengapa jawaban ini masih sangat jauh ?! The __slots__solusi adalah begitu elegan dan efektif. Dari semua yang saya baca, ini sedekat mungkin dengan membuat konstanta dengan Python. Terima kasih banyak. Dan untuk semua orang yang tertarik, inilah penjelasan __slots__ajaib dan mendalam .
JohnGalt
34

Python tidak memiliki konstanta.

Mungkin alternatif termudah adalah mendefinisikan fungsi untuknya:

def MY_CONSTANT():
    return 42

MY_CONSTANT() sekarang memiliki semua fungsi konstanta (ditambah beberapa kawat gigi yang mengganggu).

Saeed Baig
sumber
1
Saya hanya ingin menambahkan saran ini tetapi untungnya saya menggulir ke bawah ke jawaban yang berperingkat rendah. Saya berharap ini akan lebih ditingkatkan dan saya sepenuhnya setuju bahwa ia memiliki semua fungsi konstan dan sangat sederhana dan mudah. Melihat jumlah kode boilerplate di semua solusi canggih saya menemukan kawat gigi yang relatif tidak mengganggu.
yaccob
1
ini adalah jawaban yang paling sederhana, meskipun harus dicatat ini memiliki beberapa overhead dan tidak akan menghentikan orang idiot memodifikasi nilai kembali. Ini hanya akan mencegah kode lebih lanjut di baris mengubah sumber
MrMesees
@ MrMesees memodifikasi nilai kembali? Apakah maksud Anda mengedit sumber? Tetapi dari ini Anda tidak dilindungi bahkan dalam C ++, di mana konstanta (seperti constexpr) adalah konstanta keras nyata.
Ruslan
@Ruslan yang saya maksudkan adalah karena python tidak memiliki constexpr, itu tidak akan menghentikan nilai yang diedit setelah dikembalikan ke konteks luar. Tidak ada yang dilakukan untuk memperkuat keadaan beku dalam contoh ini.
MrMesees
20

Selain dua jawaban teratas (cukup gunakan variabel dengan nama UPPERCASE, atau gunakan properti untuk membuat nilai hanya baca), saya ingin menyebutkan bahwa mungkin menggunakan metaclasses untuk mengimplementasikan konstanta bernama . Saya memberikan solusi yang sangat sederhana menggunakan metaclasses di GitHub yang mungkin membantu jika Anda ingin nilai lebih informatif tentang jenis / nama mereka:

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

Ini sedikit lebih maju Python, tetapi masih sangat mudah digunakan dan praktis. (Modul ini memiliki beberapa fitur lainnya, termasuk konstanta yang hanya dapat dibaca, lihat README-nya.)

Ada solusi serupa yang beredar di berbagai repositori, tetapi sejauh yang saya ketahui mereka tidak memiliki salah satu fitur dasar yang saya harapkan dari konstanta (seperti konstan, atau sedang jenis sewenang-wenang), atau mereka memiliki fitur esoterik menambahkan bahwa membuatnya kurang umum berlaku. Tapi YMMV, saya akan berterima kasih atas umpan balik. :-)

hans_meine
sumber
3
Saya suka penerapan Anda di GitHub. Saya hampir siap untuk menulis kelas dasar yang menerapkan fungsi pencarian terbalik, tetapi saya melihat Anda telah melakukan itu dan banyak lagi!
Kerr
Terima kasih, @ Kerr, ini umpan balik pertama yang saya dapatkan dan membuat saya bahagia. :-)
hans_meine
Luar biasa. Saya baru saja mencoba ini. Senang memiliki ini sebagai opsi. Meskipun belum memutuskan apakah saya cukup peduli dengan aspek read-only, untuk menggunakan ini daripada hanya melakukan def enum(**enums): return type('Enum', (), enums). Numbers = enum(ONE=1, TWO=2, THREE='three'), sesuai stackoverflow.com/a/1695250/199364 , bagian "Dalam versi sebelumnya ..."
ToolmakerSteve
19

Properti adalah salah satu cara untuk membuat konstanta. Anda dapat melakukannya dengan mendeklarasikan properti pengambil, tetapi mengabaikan penyetel. Sebagai contoh:

class MyFinalProperty(object):

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

Anda dapat melihat artikel yang saya tulis untuk menemukan lebih banyak cara untuk menggunakan properti Python.

nvd
sumber
Di bawah solusi yang dihargai. Saya baru saja mengimplementasikan ini setelah menemukan halaman ini (bukan jawaban ini) dan berputar kembali untuk menambahkannya jika belum. Saya ingin menggarisbawahi kegunaan jawaban ini.
Marc
18

Sunting: Menambahkan kode sampel untuk Python 3

Catatan: jawaban lain ini sepertinya memberikan implementasi yang jauh lebih lengkap mirip dengan yang berikut (dengan lebih banyak fitur).

Pertama, buat metaclass :

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

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

Ini mencegah properti statika dari yang diubah. Kemudian buat kelas lain yang menggunakan metaclass itu:

class Const(object):
    __metaclass__ = MetaConst

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

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

Atau, jika Anda menggunakan Python 3:

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

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

Ini harus mencegah alat peraga instance tidak diubah. Untuk menggunakannya, mewarisi:

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

Sekarang alat peraga, diakses secara langsung atau melalui sebuah instance, harus konstan:

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

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

Berikut adalah contoh tindakan di atas. Berikut contoh lain untuk Python 3.

kabel ganda
sumber
10

Anda bisa menggunakan namedtuple sebagai solusi untuk secara efektif membuat konstanta yang bekerja dengan cara yang sama seperti variabel final statis di Jawa ("konstanta" Java). Saat penyelesaiannya, itu agak elegan. (Pendekatan yang lebih elegan adalah dengan hanya meningkatkan bahasa Python --- bahasa apa yang memungkinkan Anda mendefinisikan ulang math.pi? - tapi saya ngelantur.)

(Saat saya menulis ini, saya menyadari jawaban lain untuk pertanyaan ini bernama namedtuple, tetapi saya akan melanjutkan di sini karena saya akan menunjukkan sintaks yang lebih mirip dengan apa yang Anda harapkan di Jawa, karena tidak perlu membuat nama) ketik sebagai namedtuple memaksa Anda untuk melakukan.)

Mengikuti contoh Anda, Anda akan ingat bahwa di Java kita harus mendefinisikan konstanta di dalam beberapa kelas ; karena Anda tidak menyebutkan nama kelas, sebut saja Foo. Inilah kelas Java:

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

Inilah Python yang setara.

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

Poin kunci yang ingin saya tambahkan di sini adalah bahwa Anda tidak memerlukan Foojenis yang terpisah ("tuple anonim bernama" akan lebih baik, meskipun itu terdengar seperti sebuah oxymoron), jadi kami beri nama namesuple kami _Foosehingga mudah-mudahan tidak akan melarikan diri untuk mengimpor modul.

Poin kedua di sini adalah kita segera membuat instance nametuple, menyebutnya Foo; tidak perlu melakukan ini dalam langkah terpisah (kecuali jika Anda mau). Sekarang Anda dapat melakukan apa yang dapat Anda lakukan di Jawa:

>>> Foo.CONST_NAME
'Name'

Tetapi Anda tidak dapat menetapkan untuk itu:

>>> Foo.CONST_NAME = 'bar'

AttributeError: can't set attribute

Pengakuan: Saya pikir saya menemukan pendekatan namesuple, tetapi kemudian saya melihat bahwa orang lain memberikan jawaban yang serupa (meskipun kurang kompak). Lalu saya juga memperhatikan Apa itu "tuple bernama" dengan Python? , yang menunjukkan bahwa sys.version_infosekarang ini adalah namesuple, jadi mungkin pustaka standar Python sudah membuat ide ini jauh lebih awal.

Perhatikan bahwa sayangnya (ini masih Python), Anda dapat menghapus seluruh Footugas secara bersamaan:

>>> Foo = 'bar'

(Telapak tangan)

Tetapi setidaknya kita mencegah Foo.CONST_NAMEnilai dari perubahan, dan itu lebih baik daripada tidak sama sekali. Semoga berhasil.

Garret Wilson
sumber
Terima kasih telah mengemukakan pendekatan namesuple. Sangat inovatif. Anda mungkin juga menemukan "komentar" saya di sini relevan.
RayLuo
10

PEP 591 memiliki kualifikasi 'final'. Penegakan turun ke pemeriksa tipe.

Jadi kamu bisa melakukan:

MY_CONSTANT: Final = 12407

Catatan: Final kata kunci hanya berlaku untuk versi Python 3.8

Mike Amy
sumber
9

Berikut ini adalah implementasi dari kelas "Konstanta", yang membuat instance dengan atribut read-only (constant). Misalnya dapat digunakan Nums.PIuntuk mendapatkan nilai yang telah diinisialisasi sebagai 3.14159, dan Nums.PI = 22menimbulkan pengecualian.

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

Berkat FrozenDict dari @MikeGraham , yang saya gunakan sebagai titik awal. Diubah, jadi alih-alih Nums['ONE']sintaks penggunaannya Nums.ONE.

Dan terima kasih atas jawaban @ Raufio, untuk ide menimpa __ setattr __.

Atau untuk implementasi dengan lebih banyak fungsi, lihat @Hans_meine named_constants di GitHub

ToolmakerSteve
sumber
2
Python adalah bahasa yang menyetujui orang dewasa. Tidak ada perlindungan terhadap sesuatu seperti ini. Nums._d['PI'] = 22 Bahasa itu sendiri tidak menyediakan cara untuk menandai hal-hal sebagai non-mutables, saya percaya.
Ajay M
8

Sebuah tuple secara teknis memenuhi syarat sebagai konstanta, karena tuple akan memunculkan kesalahan jika Anda mencoba mengubah salah satu nilainya. Jika Anda ingin mendeklarasikan tuple dengan satu nilai, maka tempatkan koma setelah satu-satunya nilainya, seperti ini:

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

Untuk memeriksa nilai variabel ini, gunakan sesuatu yang mirip dengan ini:

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

Jika Anda mencoba mengubah nilai ini, kesalahan akan dinaikkan.

tobahhh
sumber
7

Saya akan membuat kelas yang menimpa __setattr__metode kelas objek dasar dan membungkus konstanta saya dengan itu, perhatikan bahwa saya menggunakan 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)

Untuk membungkus sebuah string:

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

Ini cukup sederhana, tetapi jika Anda ingin menggunakan konstanta Anda sama seperti Anda akan objek non-konstan (tanpa menggunakan constObj.value), itu akan sedikit lebih intensif. Ada kemungkinan bahwa ini dapat menyebabkan masalah, jadi mungkin yang terbaik adalah menjaga .valueagar menunjukkan dan mengetahui bahwa Anda melakukan operasi dengan konstanta (mungkin bukan cara yang paling 'pythonic').

Raufio
sumber
+1 untuk pendekatan yang menarik. Meski tidak sebersih jawaban yang sudah disediakan. Dan bahkan solusi yang disarankan sebelumnya yang paling sederhana lebih def ONE(): return 1mudah digunakan ONE()daripada jawaban ini ONE.value.
ToolmakerSteve
7

Sayangnya Python belum memiliki konstanta dan itu memalukan. ES6 sudah menambahkan konstanta dukungan ke JavaScript ( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/const ) karena itu adalah hal yang sangat berguna dalam bahasa pemrograman apa pun. Seperti dijawab dalam jawaban lain dalam komunitas Python menggunakan konvensi - variabel huruf besar pengguna sebagai konstanta, tetapi tidak melindungi terhadap kesalahan sewenang-wenang dalam kode. Jika Anda suka, Anda mungkin menemukan solusi file tunggal yang berguna sebagai berikut (lihat dokumentasi bagaimana menggunakannya).

file 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()

Jika ini tidak cukup, lihat testcase lengkap untuk itu.

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

Keuntungan: 1. Akses ke semua konstanta untuk seluruh proyek 2. Kontrol ketat untuk nilai-nilai konstanta

Kekurangan: 1. Tidak mendukung untuk jenis khusus dan jenis 'dict'

Catatan:

  1. Diuji dengan Python3.4 dan Python3.5 (saya menggunakan 'tox' untuk itu)

  2. Lingkungan pengujian:

.

$ 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
PADYMKO
sumber
Anda dapat meningkatkan sedikit ini dengan secara otomatis mengonversi kamus ke tupel bernama
Peter Schorn
6

Cara Pythonic menyatakan "konstanta" pada dasarnya adalah variabel tingkat modul:

RED = 1
GREEN = 2
BLUE = 3

Dan kemudian tulis kelas atau fungsi Anda. Karena konstanta hampir selalu bilangan bulat, dan mereka juga tidak berubah dalam Python, Anda memiliki sedikit peluang untuk mengubahnya.

Kecuali, tentu saja, jika Anda menetapkan secara eksplisit RED = 2.

Xavier Ho
sumber
21
Ya, tetapi memblokir kemampuan untuk "menetapkan secara eksplisit RED = 2" adalah seluruh manfaat (dalam bahasa lain) untuk dapat mendeklarasikan nama variabel menjadi "konstan"!
ToolmakerSteve
6
Apakah Anda akan mendapat manfaat dari pemblokiran itu? Hal yang paling berguna tentang const biasanya adalah optimisasi kompiler yang sebenarnya bukan hal yang Python. Ingin sesuatu menjadi konstan? Hanya saja, jangan mengubahnya. Jika Anda khawatir orang lain mengubahnya, Anda bisa saja meletakkannya di luar jangkauan mereka, atau hanya menyadari bahwa, jika seseorang mengubahnya, itu masalah mereka dan mereka harus menghadapinya, bukan Anda.
Kevin
@Kevin: " Apakah manfaatnya Anda dapatkan ... ", manfaat staticmemiliki penyimpanan tunggal untuk nilai untuk semua instance kelas? Kecuali jika ada kemungkinan untuk mendeklarasikan variabel statis / kelas.
menit
8
Masalah dasarnya adalah bahwa beberapa orang mungkin melihatnya sebagai nilai yang merupakan sumber kebenaran, tidak dapat diubah, dan menggunakannya sebagai sumber kebenaran di seluruh kode mereka alih-alih memperkenalkan nilai-nilai ajaib (yang saya lihat banyak di Python) - dan orang lain mungkin melihatnya sebagai sesuatu yang mereka boleh ubah sesuka hati. Ketika seseorang mengubah variabel global, dan Anda tidak tahu di mana itu bisa berubah, dan aplikasi mogok karena RED = "biru" bukannya "merah", Anda memperkenalkan masalah yang sama sekali tidak perlu yang telah dipecahkan dengan mudah dan dipahami secara universal.
Dagrooms
5

Kita dapat membuat objek deskriptor.

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) Jika kita ingin bekerja dengan konstanta di tingkat instance maka:

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) jika kita ingin membuat konstanta hanya di tingkat kelas, kita bisa menggunakan metaclass yang berfungsi sebagai wadah untuk konstanta kita (objek deskriptor kita); semua kelas yang turun akan mewarisi konstanta kami (objek deskriptor kami) tanpa risiko apa pun yang dapat dimodifikasi.

# 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

Jika saya membuat subkelas Foo, kelas ini akan mewarisi konstanta tanpa kemungkinan memodifikasi mereka

class Bar(Foo): pass

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

Kamus python bisa berubah-ubah, jadi mereka sepertinya bukan cara yang baik untuk mendeklarasikan konstanta:

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

Berikut ini trik jika Anda menginginkan konstanta dan tidak peduli dengan nilainya:

Cukup tentukan kelas kosong.

misalnya:

class RED: 
    pass
class BLUE: 
    pass
Lym Zoy
sumber
4

Dalam python, sebuah konstanta hanyalah sebuah variabel dengan nama di semua huruf besar, dengan kata-kata yang dipisahkan oleh karakter garis bawah,

misalnya

DAYS_IN_WEEK = 7

Nilai bisa berubah, karena Anda dapat mengubahnya. Tetapi mengingat aturan untuk nama memberi tahu Anda adalah konstan, mengapa Anda melakukannya? Maksud saya, ini adalah program Anda!

Ini adalah pendekatan yang diambil melalui python. Tidak ada privatekata kunci untuk alasan yang sama. Awali nama dengan garis bawah dan Anda tahu itu dimaksudkan untuk menjadi pribadi. Kode dapat melanggar aturan .... sama seperti seorang programmer dapat menghapus kata kunci pribadi.

Python dapat menambahkan constkata kunci ... tetapi seorang programmer dapat menghapus kata kunci dan kemudian mengubah konstanta jika mereka mau, tetapi mengapa melakukan itu? Jika Anda ingin melanggar aturan, Anda bisa mengubah aturan itu. Tapi mengapa repot-repot melanggar aturan jika namanya memperjelas niatnya?

Mungkin ada beberapa unit test di mana masuk akal untuk menerapkan perubahan pada nilai? Untuk melihat apa yang terjadi selama 8 hari seminggu meskipun di dunia nyata jumlah hari dalam seminggu tidak dapat diubah. Jika bahasa menghentikan Anda membuat pengecualian jika hanya ada satu kasus ini Anda perlu melanggar aturan ... Anda kemudian harus berhenti mendeklarasikannya sebagai konstanta, meskipun masih ada konstanta dalam aplikasi, dan ada hanya satu test case ini yang melihat apa yang terjadi jika diubah.

Nama huruf besar semua memberi tahu Anda bahwa itu dimaksudkan sebagai konstanta. Itu yang penting. Bukan bahasa yang memaksa batasan pada kode yang Anda punya kekuatan untuk berubah pula.

Itulah filosofi python.

innov8
sumber
4

Tidak ada cara sempurna untuk melakukan ini. Seperti yang saya pahami kebanyakan programmer hanya akan menggunakan huruf besar pengenal, jadi PI = 3.142 dapat dengan mudah dipahami sebagai sebuah konstanta.

Di sisi lain, jika Anda menginginkan sesuatu yang benar-benar bertindak seperti konstanta, saya tidak yakin Anda akan menemukannya. Dengan apa pun yang Anda lakukan akan selalu ada cara untuk mengedit "konstan" sehingga tidak akan benar-benar konstan. Ini contoh yang sangat sederhana dan kotor:

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

Ini sepertinya akan membuat konstanta gaya PHP.

Pada kenyataannya yang diperlukan seseorang untuk mengubah nilainya adalah ini:

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

Ini sama untuk semua solusi lain yang akan Anda temukan di sini - bahkan yang pandai yang membuat kelas dan mendefinisikan kembali metode atribut set - akan selalu ada jalan di sekitar mereka. Begitulah Python.

Rekomendasi saya adalah untuk menghindari semua kerumitan dan hanya memanfaatkan pengidentifikasi Anda. Itu tidak akan benar-benar konstan, tetapi sekali lagi tidak ada yang bisa.

John
sumber
4

Ada cara yang lebih bersih untuk melakukan ini dengan namedtuple:

from collections import namedtuple


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

Contoh Penggunaan

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

Dengan pendekatan ini Anda bisa menamai konstanta Anda.

Juan Ignacio Sánchez
sumber
Untuk semua orang yang membaca ini, harap diingat bahwa, jika Anda menetapkan objek yang bisa berubah sebagai salah satu dari konstanta ini, siapa pun dapat mengubah nilai internalnya. misalnya, mari kita bar = [1, 2, 3], maka, Anda dapat melakukan hal berikut: CONSTS.bar [1] = 'a' dan itu tidak akan ditolak. Jadi berhati-hatilah dengan ini.
Juan Ignacio Sánchez
Alih-alih metode peretasan ini, yang saya buat hanya untuk bersenang-senang, saya sarankan menggunakan dekorator properti Python sebagai gantinya.
Juan Ignacio Sánchez
4

Mungkin pconst library akan membantu Anda ( 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.

sim
sumber
3

Anda dapat menggunakan StringVar atau IntVar, dll, konstanta Anda adalah const_val

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

def reverse(*args):
    const_val.set(val)
Nqobizwe
sumber
2

Anda dapat melakukannya dengan collections.namedtupledan 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
Sankey yang periang
sumber
2

(Paragraf ini dimaksudkan untuk mengomentari jawaban-jawaban itu di sana - sini , yang disebutkan namedtuple, tetapi terlalu lama untuk dimasukkan ke dalam komentar, jadi, begini saja.)

Pendekatan namedTuple yang disebutkan di atas pasti inovatif. Namun, demi kelengkapan, pada bagian akhir NamedTuple dari dokumentasi resminya , terbaca:

konstanta enumerasi dapat diimplementasikan dengan nama tupel, tetapi lebih sederhana dan lebih efisien untuk menggunakan deklarasi kelas sederhana:

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

Dengan kata lain, jenis dokumentasi resmi lebih suka menggunakan cara praktis, daripada benar-benar menerapkan perilaku read-only. Saya kira itu menjadi contoh lain dari Zen Python :

Sederhana lebih baik daripada kompleks.

kepraktisan mengalahkan kemurnian.

RayLuo
sumber
2

Ini adalah kumpulan idiom yang saya buat sebagai upaya untuk meningkatkan beberapa jawaban yang sudah tersedia.

Saya tahu penggunaan konstanta bukan pythonic, dan Anda seharusnya tidak melakukan ini di rumah!

Namun, Python adalah bahasa yang sangat dinamis! Forum ini menunjukkan bagaimana mungkin penciptaan konstruksi yang terlihat dan terasa seperti konstanta. Jawaban ini sebagai tujuan utama untuk mengeksplorasi apa yang dapat diungkapkan oleh bahasa.

Tolong jangan terlalu keras dengan saya :-).

Untuk lebih jelasnya saya menulis blog pendamping tentang idiom-idiom ini .

Dalam posting ini, saya akan memanggil variabel konstan ke referensi konstan ke nilai (tidak berubah atau sebaliknya). Selain itu, saya mengatakan bahwa variabel memiliki nilai beku ketika referensi objek yang bisa berubah yang kode klien tidak dapat memperbarui nilainya.

Sebuah ruang konstanta (SpaceConstants)

Idiom ini menciptakan apa yang tampak seperti namespace variabel konstan (alias SpaceConstants). Ini adalah modifikasi dari potongan kode oleh Alex Martelli untuk menghindari penggunaan objek modul. Secara khusus, modifikasi ini menggunakan apa yang saya sebut pabrik kelas karena dalam fungsi SpaceConstants , kelas yang disebut SpaceConstants didefinisikan, dan sebuah instance dari itu dikembalikan.

Saya menjelajahi penggunaan pabrik kelas untuk mengimplementasikan desain berbasis kebijakan yang mirip dengan Python di stackoverflow dan juga di 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"

Ruang nilai beku (SpaceFrozenValues)

Ungkapan berikut ini adalah modifikasi dari SpaceConstants di mana objek yang dapat dirujuk dibekukan. Implementasi ini memanfaatkan apa yang saya sebut bersama penutupan antara setattr dan getattr fungsi. Nilai objek yang dapat diubah disalin dan direferensikan oleh cache variabel yang mendefinisikan bagian dalam fungsi shared closure. Ini membentuk apa yang saya sebut salinan terproteksi dari objek yang bisa berubah .

Anda harus berhati-hati dalam menggunakan idiom ini karena getattr mengembalikan nilai cache dengan melakukan salinan yang dalam. Operasi ini dapat memiliki dampak kinerja yang signifikan pada objek besar!

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"

Ruang konstan (ConstantSpace)

Idiom ini adalah namespace abadi dari variabel konstan atau ConstantSpace . Ini adalah kombinasi dari jawaban Jon Betts yang sangat sederhana dalam stackoverflow dengan pabrik kelas .

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

Ruang beku (FrozenSpace)

Ungkapan ini adalah namespace abadi dari variabel beku atau FrozenSpace . Ini diturunkan dari pola sebelumnya dengan membuat setiap variabel sebagai properti yang dilindungi dengan penutupan kelas FrozenSpace yang dihasilkan .

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'"
Victor Bazterra
sumber
2

Dalam Python, konstanta tidak ada, tetapi Anda dapat menunjukkan bahwa variabel adalah konstanta dan tidak boleh diubah dengan menambahkan CONST_ke awal nama variabel dan menyatakan bahwa itu adalah konstanta dalam komentar:

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.

Atau, Anda dapat membuat fungsi yang berfungsi seperti konstanta:

def CONST_daysInWeek():
    return 7;

sumber
1

Dalam kasus saya, saya perlu bytearr yang tidak dapat diubah untuk implementasi perpustakaan crypto yang berisi banyak angka literal yang ingin saya pastikan konstan.

Jawaban ini berfungsi tetapi upaya penugasan kembali elemen bytearray tidak menimbulkan kesalahan.

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

Konstanta tidak berubah, tetapi tugas bytearray konstan gagal dalam diam:

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

Pendekatan yang lebih kuat, sederhana, dan bahkan mungkin lebih 'pythonic' melibatkan penggunaan objek memoryview (objek buffer dalam <= 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)

Tugas item ConstArray adalah 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
jxqz
sumber
1

Saya menulis lib util untuk python const: kkconst - pypi support str, int, float, datetime

turunan bidang const akan menjaga perilaku tipe dasarnya.

Sebagai contoh:

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

lebih detail penggunaannya Anda bisa membaca url pypi : pypi atau github

kaka_ace
sumber
1

Anda dapat membungkus konstanta dalam array numpy, beri flag itu hanya untuk menulis, dan selalu menyebutnya dengan indeks nol.

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

tentu saja ini hanya melindungi isi numpy, bukan variabel "KONSTAN" itu sendiri; Anda masih bisa melakukan:

CONSTANT = 'foo'

dan CONSTANTakan berubah, namun itu akan dengan cepat membuang TypeError saat pertama kali CONSTANT[0]dipanggil dalam skrip.

meskipun ... Saya kira jika Anda pada suatu titik mengubahnya menjadi

CONSTANT = [1,2,3]

sekarang Anda tidak akan mendapatkan TypeError lagi. hmmmm ....

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

kehadiran kecil
sumber