Salt okunur özellikler oluşturmak için önceki iki yanıttan memnun değilim çünkü ilk çözüm salt okunur özniteliğin silinmesine ve sonra ayarlanmasına izin veriyor ve __dict__ öğesini engellemiyor. İkinci çözüm, test etmekle, ikiye ayarladığınız şeye eşit olan değeri bulmak ve sonunda değiştirmekle çalışılabilir.
Şimdi, kod için.
def final(cls):
clss = cls
@classmethod
def __init_subclass__(cls, **kwargs):
raise TypeError("type '{}' is not an acceptable base type".format(clss.__name__))
cls.__init_subclass__ = __init_subclass__
return cls
def methoddefiner(cls, method_name):
for clss in cls.mro():
try:
getattr(clss, method_name)
return clss
except(AttributeError):
pass
return None
def readonlyattributes(*attrs):
"""Method to create readonly attributes in a class
Use as a decorator for a class. This function takes in unlimited
string arguments for names of readonly attributes and returns a
function to make the readonly attributes readonly.
The original class's __getattribute__, __setattr__, and __delattr__ methods
are redefined so avoid defining those methods in the decorated class
You may create setters and deleters for readonly attributes, however
if they are overwritten by the subclass, they lose access to the readonly
attributes.
Any method which sets or deletes a readonly attribute within
the class loses access if overwritten by the subclass besides the __new__
or __init__ constructors.
This decorator doesn't support subclassing of these classes
"""
def classrebuilder(cls):
def __getattribute__(self, name):
if name == '__dict__':
from types import MappingProxyType
return MappingProxyType(super(cls, self).__getattribute__('__dict__'))
return super(cls, self).__getattribute__(name)
def __setattr__(self, name, value):
if name == '__dict__' or name in attrs:
import inspect
stack = inspect.stack()
try:
the_class = stack[1][0].f_locals['self'].__class__
except(KeyError):
the_class = None
the_method = stack[1][0].f_code.co_name
if the_class != cls:
if methoddefiner(type(self), the_method) != cls:
raise AttributeError("Cannot set readonly attribute '{}'".format(name))
return super(cls, self).__setattr__(name, value)
def __delattr__(self, name):
if name == '__dict__' or name in attrs:
import inspect
stack = inspect.stack()
try:
the_class = stack[1][0].f_locals['self'].__class__
except(KeyError):
the_class = None
the_method = stack[1][0].f_code.co_name
if the_class != cls:
if methoddefiner(type(self), the_method) != cls:
raise AttributeError("Cannot delete readonly attribute '{}'".format(name))
return super(cls, self).__delattr__(name)
clss = cls
cls.__getattribute__ = __getattribute__
cls.__setattr__ = __setattr__
cls.__delattr__ = __delattr__
cls = final(cls)
return cls
return classrebuilder
def setreadonlyattributes(cls, *readonlyattrs):
return readonlyattributes(*readonlyattrs)(cls)
if __name__ == '__main__':
@readonlyattributes('readonlyfield')
class ReadonlyFieldClass(object):
def __init__(self, a, b):
self.readonlyfield = a
self.publicfield = b
attr = None
def main():
global attr
pfi = ReadonlyFieldClass('forbidden', 'changable')
try:
print(pfi.publicfield)
print('__getattribute__ works')
pfi.publicfield = 'mutable'
print('__setattr__ seems to work')
print(pfi.publicfield)
print('__setattr__ definitely works')
del pfi.publicfield
print('__delattr__ seems to work')
print(pfi.publlicfield)
raise RuntimeError('__delattr__ doesn\'t work')
except(AttributeError):
print('__delattr__ works')
try:
print(pfi.readonlyfield)
print('__getattribute__ works')
pfi.readonlyfield = 'readonly'
raise RuntimeError('__setattr__ doesn\'t work')
except(AttributeError):
print('__setattr__ seems to work')
try:
print(pfi.readonlyfield)
print('__setattr__ works')
del pfi.readonlyfield
raise RuntimeError('__delattr__ doesn\'t work')
except(AttributeError):
print('__delattr__ works')
try:
print("Dict testing")
print(pfi.__dict__, type(pfi.__dict__))
attr = pfi.readonlyfield
print(attr)
print("__getattribute__ works")
if pfi.readonlyfield != 'forbidden':
print(pfi.readonlyfield)
raise RuntimeError("__getattr__ doesn't work")
try:
pfi.__dict__ = {}
raise RuntimeError("__setattr__ doesn't work")
except(AttributeError):
print("__setattr__ works")
del pfi.__dict__
raise RuntimeError("__delattr__ doesn't work")
except(AttributeError):
print(pfi.__dict__)
print("__delattr__ works")
print("Basic things work")
main()
Uygulama geliştirme gibi başka herhangi bir amaç için kod değil, programlarını geliştirmek için kullanmak üzere başkalarına kod olarak dağıtılan kitaplık kodu yazmanız dışında salt okunur nitelikler yapmanın bir anlamı yoktur . __Dict__ problemi çözüldü, çünkü __dict__ artık değişmez türlerden.MappingProxyType , bu nedenle öznitelikler __dict__ ile değiştirilemez. __Dict__’ın ayarlanması veya silinmesi de engellenir. Salt okunur özellikleri değiştirmenin tek yolu, sınıfın kendi yöntemlerini değiştirmektir.
Çözümümün önceki ikisinden daha iyi olduğuna inansam da iyileştirilebilir. Bunlar bu kodun zayıf yönleridir:
a) Bir alt sınıfta salt okunur bir özniteliği ayarlayan veya silen bir yönteme eklemeye izin vermez. Bir alt sınıfta tanımlanan bir yöntemin, yöntemin süper sınıf sürümünün çağrılmasıyla bile, salt okunur özelliğe erişmesi otomatik olarak engellenir.
b) Sınıfın salt okunur yöntemleri, salt okunur kısıtlamaları ortadan kaldırmak için değiştirilebilir.
Ancak, sınıfı düzenlemeden salt okunur bir niteliği ayarlamanın veya silmenin yolu yoktur. Bu, adlandırma kurallarına bağlı değildir, bu iyidir çünkü Python adlandırma kurallarıyla o kadar tutarlı değildir. Bu, sınıfın kendisini düzenlemeden gizli boşluklarla değiştirilemeyen salt okunur nitelikler yapmanın bir yolunu sağlar. Sadece dekoratör argüman olarak çağrıldığında okunacak nitelikleri listeleyin ve bunlar salt okunur hale gelecektir.
Python'da başka bir sınıfın bir işlevi içinde arayan sınıfın adı nasıl alınır? arayan sınıflarını ve yöntemlerini almak için.
self.x
ve kimsenin değişmeyeceğine güveninx
.x
Bunun değiştirilemeyeceğinden emin olmak önemliyse, bir özellik kullanın.