Python'un tanımlayıcılarının ne olduğunu ve ne için yararlı olabileceklerini anlamaya çalışıyorum.
Tanımlayıcılar, aşağıdaki özel yöntemlerden herhangi birine sahip sınıf nitelikleridir (özellikler veya yöntemler gibi):
__get__
(örneğin bir yöntem / işlevde veri olmayan tanımlayıcı yöntem)
__set__
(örneğin bir özellik örneğinde veri tanımlayıcı yöntemi)
__delete__
(veri tanımlayıcı yöntem)
Bu tanımlayıcı nesneler, diğer nesne sınıfı tanımlarında öznitelik olarak kullanılabilir. (Yani, __dict__
sınıf nesnesinin içinde yaşarlar .)
Tanımlayıcı nesneler, foo.descriptor
normal ifadede, atamada ve hatta silmede noktalı aramanın (örn. ) Sonuçlarını programlı olarak yönetmek için kullanılabilir .
Fonksiyonlar / yöntemleri, sınır yöntemleri, property
, classmethod
ve staticmethod
tüm kullanım onlar noktalı araması ile erişilen nasıl kontrole bu özel yöntemler.
Örneğin , bir veri tanımlayıcı , property
nesnelerin daha basit bir durumuna dayalı olarak tembel özelliklerin değerlendirilmesine izin vererek, örneklerin olası her özniteliği önceden hesapladığınızdan daha az bellek kullanmasına izin verebilir.
Başka bir veri tanımlayıcısı olan a member_descriptor
, __slots__
sınıfın verileri daha esnek ancak yer kaplayan yerine değiştirilebilir bir grup benzeri veri yapısında depolamasına izin vererek bellek tasarrufuna izin verir __dict__
.
Veri olmayan tanımlayıcılar, genellikle örnek, sınıf ve statik yöntemler, örtük ilk bağımsız değişkenlerini (genellikle adlandırılmış cls
ve self
sırasıyla) veri olmayan tanımlayıcı yöntemlerinden alırlar __get__
.
Çoğu Python kullanıcısının sadece basit kullanımı öğrenmesi ve tanımlayıcıların uygulanmasını daha fazla öğrenmesi veya anlaması gerekmez.
Derinlemesine: Tanımlayıcılar Nelerdir?
Bir tanımlayıcı, bir örneğin tipik bir özniteliği gibi noktalı arama yoluyla kullanılması amaçlanan aşağıdaki yöntemlerden ( __get__
, __set__
veya __delete__
) herhangi birine sahip bir nesnedir . obj_instance
Bir descriptor
nesne ile birlikte bir sahip- nesne için:
obj_instance.descriptor
descriptor.__get__(self, obj_instance, owner_class)
döndürme çağırır a Bir özellik value
tüm yöntemleri ve açık nasıl get
çalışır.
obj_instance.descriptor = value
descriptor.__set__(self, obj_instance, value)
döndürme çağırır None
Bu setter
bir özellik üzerinde çalışır.
del obj_instance.descriptor
descriptor.__delete__(self, obj_instance)
döndürme çağırır None
Bu deleter
bir özellik üzerinde çalışır.
obj_instance
sınıfı tanımlayıcı nesnenin örneğini içeren örnektir. tanımlayıcınınself
örneğidir (muhtemelen sınıfının yalnızca biri )obj_instance
Bunu kodla tanımlamak için, öznitelikler kümesi gerekli özniteliklerden herhangi biriyle kesişiyorsa bir nesne bir tanımlayıcıdır:
def has_descriptor_attrs(obj):
return set(['__get__', '__set__', '__delete__']).intersection(dir(obj))
def is_descriptor(obj):
"""obj can be instance of descriptor or the descriptor class"""
return bool(has_descriptor_attrs(obj))
Bir Veri Tanımlayıcı bir sahiptir __set__
ve / veya __delete__
.
Bir Sigara Veri tanımlayıcısı ikiside yok __set__
ne de __delete__
.
def has_data_descriptor_attrs(obj):
return set(['__set__', '__delete__']) & set(dir(obj))
def is_data_descriptor(obj):
return bool(has_data_descriptor_attrs(obj))
Yerleşik Tanımlayıcı Nesne Örnekleri:
classmethod
staticmethod
property
- genel olarak fonksiyonlar
Veri Dışı Tanımlayıcılar
Bunu görebiliriz classmethod
ve staticmethod
Veri Tanımlayıcı Olmayanlardır:
>>> is_descriptor(classmethod), is_data_descriptor(classmethod)
(True, False)
>>> is_descriptor(staticmethod), is_data_descriptor(staticmethod)
(True, False)
Her ikisi de sadece __get__
yönteme sahiptir:
>>> has_descriptor_attrs(classmethod), has_descriptor_attrs(staticmethod)
(set(['__get__']), set(['__get__']))
Tüm işlevlerin aynı zamanda Veri Tanımlayıcı Olmayanlar olduğuna dikkat edin:
>>> def foo(): pass
...
>>> is_descriptor(foo), is_data_descriptor(foo)
(True, False)
Veri Tanımlayıcı, property
Ancak, property
bir Veri Tanımlayıcıdır:
>>> is_data_descriptor(property)
True
>>> has_descriptor_attrs(property)
set(['__set__', '__get__', '__delete__'])
Noktalı Arama Sırası
Bunlar, noktalı bir aramanın arama sırasını etkilediği için önemli ayrımlardır .
obj_instance.attribute
- İlk olarak yukarıdakiler, özelliğin örneğin sınıfında bir Veri Tanımlayıcı olup olmadığını görmek için,
- Değilse, özelliğin
obj_instance
's'de olup olmadığına bakar __dict__
,
- Sonunda bir Veri Tanımlayıcı Olmayan'a geri döner.
Bu arama sırasının sonucu, işlevler / yöntemler gibi Veri Tanımlayıcı Olmayanların örnekler tarafından geçersiz kılınabilmesidir .
Özet ve Sonraki Adımlar
Biz tanımlayıcılar herhangi biriyle nesneler olduğunu öğrendik __get__
, __set__
ya __delete__
. Bu tanımlayıcı nesneler, diğer nesne sınıfı tanımlarında öznitelik olarak kullanılabilir. Şimdi kodunuzu örnek olarak kullanarak nasıl kullanıldıklarına bakacağız.
Kodun Sorudan Analizi
İşte kodunuz, ardından sorularınız ve her birine cevaplarınız:
class Celsius(object):
def __init__(self, value=0.0):
self.value = float(value)
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
self.value = float(value)
class Temperature(object):
celsius = Celsius()
- Neden tanımlayıcı sınıfına ihtiyacım var?
Tanımlayıcınız, bu sınıf özniteliği için her zaman bir kayan noktaya sahip olmanızı ve özelliği silmek için Temperature
kullanamayacağınızı garanti eder del
:
>>> t1 = Temperature()
>>> del t1.celsius
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: __delete__
Aksi takdirde, tanımlayıcılarınız durumu tanımlayıcıda depolamak yerine sahip sınıfını ve sahibin örneklerini yok sayar. Basit bir sınıf özniteliğiyle tüm örneklerde durumu kolayca paylaşabilirsiniz (bunu her zaman sınıfa bir kayan nokta olarak ayarlayıp asla silmediğiniz veya kodunuzun kullanıcıları ile rahat olduğunuz sürece):
class Temperature(object):
celsius = 0.0
Bu, örneğinizle tam olarak aynı davranışı sağlar (aşağıdaki 3. soruya verilen cevaba bakın), ancak bir Pythons yerleşik ( property
) kullanır ve daha deyimsel olarak kabul edilir:
class Temperature(object):
_celsius = 0.0
@property
def celsius(self):
return type(self)._celsius
@celsius.setter
def celsius(self, value):
type(self)._celsius = float(value)
- Burada örnek ve sahip nedir? ( olsun ). Bu parametrelerin amacı nedir?
instance
tanımlayıcıyı çağıran sahibin örneğidir. Sahip, tanımlayıcı nesnenin veri noktasına erişimi yönetmek için kullanıldığı sınıftır. Daha açıklayıcı değişken adları için bu cevabın ilk paragrafının yanındaki tanımlayıcıları tanımlayan özel yöntemlerin açıklamalarına bakın.
- Bu örneği nasıl arayabilirim / kullanabilirim?
İşte bir gösteri:
>>> t1 = Temperature()
>>> t1.celsius
0.0
>>> t1.celsius = 1
>>>
>>> t1.celsius
1.0
>>> t2 = Temperature()
>>> t2.celsius
1.0
Özelliği silemezsiniz:
>>> del t2.celsius
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: __delete__
Ve bir kayan noktaya dönüştürülemeyen bir değişken atayamazsınız:
>>> t1.celsius = '0x02'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in __set__
ValueError: invalid literal for float(): 0x02
Aksi takdirde, burada bulunanlar, tüm örnekler için, herhangi bir örneğe atanarak yönetilen küresel bir durumdur.
En deneyimli Python programcılarının bu sonucu elde etmesinin beklenen yolu property
, başlık altında aynı tanımlayıcıları kullanan ancak davranışı sahip sınıfının uygulanmasına getiren dekoratörü kullanmak olacaktır (yine yukarıda tanımlandığı gibi):
class Temperature(object):
_celsius = 0.0
@property
def celsius(self):
return type(self)._celsius
@celsius.setter
def celsius(self, value):
type(self)._celsius = float(value)
Orijinal kod parçasının beklenen davranışı tam olarak aynıdır:
>>> t1 = Temperature()
>>> t2 = Temperature()
>>> t1.celsius
0.0
>>> t1.celsius = 1.0
>>> t2.celsius
1.0
>>> del t1.celsius
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't delete attribute
>>> t1.celsius = '0x02'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 8, in celsius
ValueError: invalid literal for float(): 0x02
Sonuç
Tanımlayıcıları tanımlayan özellikleri, veri ve veri tanımlayıcı olmayanlar arasındaki farkı, bunları kullanan yerleşik nesneleri ve kullanımla ilgili belirli soruları ele aldık.
Peki yine, sorunun örneğini nasıl kullanırsınız? Umarım yapmazsın. Umarım ilk önerim (basit bir sınıf özniteliği) ile başlar ve gerekli olduğunu düşünüyorsanız ikinci öneriye (özellik dekoratörü) geçersiniz.
self
veinstance
?