Python'da özellik özelliğinin nasıl kullanılacağına dair gerçek dünya örneği?


143

@propertyPython'da nasıl kullanacağımla ilgileniyorum . Python belgelerini okudum ve oradaki örnek, bence, sadece bir oyuncak kodu:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

_xMülk dekoratörü ile doldurulmuştan ne kadar fayda sağlayacağımı bilmiyorum . Neden sadece şu şekilde uygulamaz:

class C(object):
    def __init__(self):
        self.x = None

Özellik özelliği bazı durumlarda yararlı olabilir düşünüyorum. Ama ne zaman? Birisi bana bazı gerçek dünya örnekleri verebilir mi?

Teşekkürler.


10
Bu özellik dekoratör hakkında bulduğum en iyi ve en temiz açıklama [buraya tıklayın ]
Sumudu

2
@Anubis, sağladığınız bağlantıdaki son örnekte, c = Santigrat (-500) ayarının, istenen sonucu elde etmediğini düşündüğüm herhangi bir ValueError atmadı.
Sajuuk

@Anubis ile aynı fikirde. Burada doğru bir şekilde uygulanmıştır: python-course.eu/python3_properties.php
anon01

Yanıtlar:


92

Diğer örnekler, ayarlanan niteliklerin validasyonu / filtrelenmesi (bunların sınırlar içinde veya kabul edilebilir olmaya zorlanması) ve karmaşık veya hızla değişen terimlerin tembel değerlendirilmesi olabilir.

Bir özelliğin arkasına gizlenmiş karmaşık hesaplama:

class PDB_Calculator(object):
    ...
    @property
    def protein_folding_angle(self):
        # number crunching, remote server calls, etc
        # all results in an angle set in 'some_angle'
        # It could also reference a cache, remote or otherwise,
        # that holds the latest value for this angle
        return some_angle

>>> f = PDB_Calculator()
>>> angle = f.protein_folding_angle
>>> angle
44.33276

Doğrulama:

class Pedometer(object)
    ...
    @property
    def stride_length(self):
        return self._stride_length

    @stride_length.setter
    def stride_length(self, value):
        if value > 10:
            raise ValueError("This pedometer is based on the human stride - a stride length above 10m is not supported")
        else:
            self._stride_length = value

1
PDB_Calculator örneğini seviyorum - karmaşık şeyler soyutlandı, her şey çalışıyor ve kullanıcı basitliğin tadını çıkarabilir!
Adam Kurkiewicz

2
muhtemelen, profesyonel açıdan, bunlar çok iyi örneklerdir. Ancak, bir noobie olarak, bu örnekleri oldukça etkisiz buluyorum. benim kötü ... :(
kmonsoor

78

Basit bir kullanım örneği, _xpython'da bir alt çizgiye sahip bir değişken adı yönlendirmenin genellikle özel (dahili kullanım) anlamına geldiğini bildiğiniz için salt okunur bir örnek niteliği ayarlamak olacaktır, ancak bazen örnek niteliğini okuyabilir ve yazamayız bunun propertyiçin kullanabiliriz :

>>> class C(object):

        def __init__(self, x):
            self._x = x

        @property
        def x(self):
            return self._x

>>> c = C(1)
>>> c.x
1
>>> c.x = 2
AttributeError        Traceback (most recent call last)

AttributeError: can't set attribute

7
c._xKullanıcı isterse hala ayarlanabilir . Python aslında gerçek özel özelliklere sahip değildir.

21

Çok pratik bir kullanım için bu makaleye göz atın . Kısacası, Python'da genellikle açık alıcı / ayarlayıcı yöntemini nasıl atabileceğinizi açıklar, çünkü bir aşamada onlara ihtiyacınız olursa propertykesintisiz bir uygulama için kullanabilirsiniz .


15

Bunu kullandığım bir şey, yavaş arama, ancak değişmeyen değerleri bir veritabanında saklanan önbelleğe almaktır. Bu, özniteliklerinizin hesaplama veya yalnızca talep üzerine yapmak istediğiniz başka bir uzun işlem (örn. Veritabanı denetimi, ağ iletişimi) gerektirdiği herhangi bir durum için geneldir.

class Model(object):

  def get_a(self):
    if not hasattr(self, "_a"):
      self._a = self.db.lookup("a")
    return self._a

  a = property(get_a)

Bu, herhangi bir sayfa görünümünün bu türden yalnızca belirli bir özelliğe ihtiyaç duyabileceği bir web uygulamasındaydı, ancak altta yatan nesnelerin kendileri bu tür birkaç özelliğe sahip olabilir - hepsini inşaatta başlatmak atık olur ve özellikler esnek olmamı sağlar. nitelikleri tembel ve hangisi değil.


1
Bunun @cached_propertyiçin kullanamaz mısın?
adarsh

@adarsh ​​- Kulağa ilginç geliyor. Nerede bu?
14'te


2
İlginç. Sanırım ilk olarak bu cevaptan sonra yayınlandı, ancak bunu okuyan herkes muhtemelen kullanmalıdır.
14'te

10

Cevapları ve yorumları okurken, ana tema cevapların basit ama kullanışlı bir örneği eksik gibi görünüyor. Buraya @propertydekoratörün basit kullanımını gösteren çok basit bir tane ekledim. Kullanıcının çeşitli farklı birimler kullanarak mesafe ölçümü belirlemesini ve almasını sağlayan bir sınıftır, yani in_feetveya in_metres.

class Distance(object):
    def __init__(self):
        # This private attribute will store the distance in metres
        # All units provided using setters will be converted before
        # being stored
        self._distance = 0.0

    @property
    def in_metres(self):
        return self._distance

    @in_metres.setter
    def in_metres(self, val):
        try:
            self._distance = float(val)
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

    @property
    def in_feet(self):
        return self._distance * 3.2808399

    @in_feet.setter
    def in_feet(self, val):
        try:
            self._distance = float(val) / 3.2808399
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

    @property
    def in_parsecs(self):
        return self._distance * 3.24078e-17

    @in_parsecs.setter
    def in_parsecs(self, val):
        try:
            self._distance = float(val) / 3.24078e-17
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

Kullanımı:

>>> distance = Distance()
>>> distance.in_metres = 1000.0
>>> distance.in_metres
1000.0
>>> distance.in_feet
3280.8399
>>> distance.in_parsecs
3.24078e-14

şahsen benim için, getters / setters'ın en iyi örnekleri insanlara daha sonra yapmanız gereken değişiklikleri göstermektir, ancak açıkçası, bu biraz daha zaman alır.
dtc

7

Özellik, belirli bir alanın manipüle edilebileceği ve ara katman hesaplamaları yapabileceğiniz yollar üzerinde daha fazla kontrol sahibi olmanızı sağlayan bir alanın çevresindeki bir soyutlamadır. Akla gelen kullanımlardan birkaçı doğrulama ve ön başlatma ve erişim kısıtlamasıdır.

@property
def x(self):
    """I'm the 'x' property."""
    if self._x is None:
        self._x = Foo()

    return self._x

6

Evet, yayınlanan orijinal örnek için özellik, yalnızca 'x' örnek değişkenine sahip olmakla tamamen aynı şekilde çalışır.

Python özellikleri hakkında en iyi şey budur. Dışarıdan, örnek değişkenler gibi çalışırlar! Bu, sınıf dışından örnek değişkenleri kullanmanıza olanak tanır.

Bu, ilk örneğinizin aslında bir örnek değişkeni kullanabileceği anlamına gelir. İşler değiştiyse ve sonra uygulamanızı değiştirmeye karar verirseniz ve bir özellik yararlıysa, özelliğin arabirimi sınıf dışındaki kodla aynı kalır. Örnek değişkeninden özelliğe yapılan değişikliğin sınıf dışındaki kod üzerinde bir etkisi yoktur.

Diğer birçok dil ve programlama kursu, bir programcının asla örnek değişkenleri göstermemesini ve bunun yerine sınıfın dışından erişilecek herhangi bir değere (hatta soruda belirtildiği gibi basit duruma) 'getters' ve 'setters' kullanmamasını söyleyecektir.

Sınıf dışında birçok dil (ör. Java) kullanımı ile kodlama

object.get_i()
    #and
object.set_i(value)

#in place of (with python)
object.i
    #and 
object.i = value

Sınıfı uygularken tam olarak ilk örneğiniz gibi birçok 'alıcı' ve 'ayarlayıcı' vardır: basit bir örnek değişkeni çoğaltın. Sınıf alıcıları değişirse, sınıf dışındaki tüm kodların değişmesi gerekeceğinden bu alıcılar ve ayarlayıcılar gereklidir. Ancak python özellikleri sınıf dışındaki kodların örnek değişkenlerle aynı olmasına izin verir. Bu nedenle, bir özellik eklerseniz veya basit bir örnek değişkeniniz varsa sınıf dışındaki kodun değiştirilmesi gerekmez. Yani en Object Oriented dillerden farklı olarak, sizin basit örneğin siz yapabilirsiniz Gelecekte bir mülke değiştirirseniz, kod kullanarak bilerek güvenli, yerine gerçekten gerekli değildir 'getters' ve 'belirleyiciler örneği değişken kullanın sınıfınızın değişmesine gerek yoktur.

Bu, yalnızca karmaşık davranışlar olduğunda özellikler oluşturmaya ihtiyacınız olduğu ve soruda açıklandığı gibi, basit bir örnek değişkeninin gerekli olduğu tek şey olduğu için, yalnızca örnek değişkenini kullanabileceğiniz anlamına gelir.


6

ayarlayıcıları ve alıcıları kullanarak özelliklerin başka bir güzel özelliği, yine de herhangi bir doğrulama, erişim kontrolü, önbellekleme vb. korurken, özelliklerinizde OP = operatörlerini (örn. ayarlayıcılar ve alıcılar tedarik edecekti.

örneğin, sınıfı Personbir ayarlayıcı setage(newage)ve alıcı ile yazdıysanız, getage()yazmanız gereken yaşı artırmak için:

bob = Person('Robert', 25)
bob.setage(bob.getage() + 1)

ama yaptıysan age bir mülk çok daha temiz yazabilirsiniz:

bob.age += 1

5

Sorunuzun kısa cevabı, örneğinizde hiçbir faydasının olmamasıdır. Muhtemelen özellikleri içermeyen formu kullanmalısınız.

Bunun nedeni, kodunuz gelecekte değişiyorsa ve birdenbire verilerinizle daha fazlasını yapmanız gerekiyorsa: önbellek değerleri, erişimi koruyun, bazı harici kaynakları sorgulayın ... her neyse, alıcıları eklemek için sınıfınızı kolayca değiştirebilirsiniz. ve veriler için belirleyiciler olmadan o accedere ve değişim bu da nereye kodunuzda her yerde bulmak zorunda kalmamak için, arayüz değişiyor.


4

Birçoğunun ilk başta fark etmediği bir şey, kendi alt sınıflarınızı yapabilmenizdir. Bu, salt okunur nesne özniteliklerini veya okuyabileceğiniz ve yazabileceğiniz ancak kaldıramayacağınız özniteliği ortaya çıkarmak için çok yararlı buldum. Nesne alanlarındaki değişiklikleri izleme gibi işlevleri sarmanın mükemmel bir yoludur.

class reader(property):
    def __init__(self, varname):
        _reader = lambda obj: getattr(obj, varname)
        super(reader, self).__init__(_reader)

class accessor(property):
    def __init__(self, varname, set_validation=None):
        _reader = lambda obj: getattr(obj, varname)
        def _writer(obj, value):
            if set_validation is not None:
               if set_validation(value):
                  setattr(obj, varname, value)
        super(accessor, self).__init__(_reader, _writer)

#example
class MyClass(object):
   def __init__(self):
     self._attr = None

   attr = reader('_attr')

Bunu severim. Bunu, okuyucunun yalnızca erişimci silme özelliği olmadan okuma / yazma sırasında okunması nedeniyle doğru okuyor muyum? Yine de veri doğrulamasını nasıl eklersiniz? Python için oldukça yeniyim ama muhtemelen attr = reader('_attr')hatta geri arama eklemenin bir yolu veya bir çeşit ön kontrol yapmanın bir yolu olduğunu düşünüyorum attr = if self.__isValid(value): reader('_attr'). Öneriler?
Gabe Spradlin

Üzgünüm, sadece salt okunur bir değişken için veri doğrulaması hakkında soru sorduğumu fark ettim. Ancak bu sadece erişimci sınıfının ayarlayıcı kısmı için geçerlidir. Yani değiştirmek attr = reader('_attr')için attr = accessor('_attr'). Teşekkürler
Gabe Spradlin

Doğrulama istiyorsanız, geçersiz duruma (veya hiçbir şey yapmamak da dahil olmak üzere sevdiğiniz her türlü davranışı) istisnayı doğrulamak ve yükseltmek için bir işlev ekleyeceğiniz konusunda haklısınız . Yukarıdakileri olası bir modelle değiştirdim. Doğrulayıcı, kümenin gerçekleşip gerçekleşmeyeceğini yönlendirmek için True | False döndürmelidir.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.