@Property dekoratör nasıl çalışır?


980

Yerleşik işlevin nasıl çalıştığını anlamak istiyorum property. Beni şaşırtan şey, propertydekoratör olarak da kullanılabilmesidir, ancak dekoratör olarak kullanıldığında değil, yalnızca yerleşik bir işlev olarak kullanıldığında argümanları alır.

Bu örnek belgelerden alınmıştır :

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

    def getx(self):
        return self._x
    def setx(self, value):
        self._x = value
    def delx(self):
        del self._x
    x = property(getx, setx, delx, "I'm the 'x' property.")

propertybireyin argümanlar vardır getx, setx, delxve bir doktor dizesi.

Aşağıdaki kodda propertydekoratör olarak kullanılır. Bunun nesnesi xişlevdir, ancak yukarıdaki kodda argümanlarda bir nesne işlevi için yer yoktur.

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

Ve x.setterve x.deleterdekoratörler nasıl oluşturulur? Kafam karıştı.



3
propertyaslında bir sınıftır (bir işlev değildir), ancak __init__()bir nesneyi oluştururken muhtemelen yöntemi çağırır, elbette. Kullanma help(property)terminalden içgörülü. helpaynı zamanda bir nedenden dolayı bir sınıftır.
Brōtsyorfuzthrāx

Sanırım bu bağlantı iyi bir örnek veriyor : [property] ( journaldev.com/14893/python-property-decorator )
Sheng Bi

4
@Shule 2 yaşındaki iş parçacığı, ama yine de: Her şey bir sınıf. Sınıflar bile.
Artemis,

2
Bu benim için de kafa karıştırıcıydı. Sonunda benim için parçalayabilecek bir makale buldum. Umarım bunun bir başkasına yardımı olur. programiz.com/python-programming/property Bu siteye hiçbir şekilde bağlı değilim.
jjwdesign

Yanıtlar:


1008

property()İşlev özel döndüren açıklayıcısı nesnesi :

>>> property()
<property object at 0x10ff07940>

Ek yöntemlere sahip olan bu nesnedir :

>>> property().getter
<built-in method getter of property object at 0x10ff07998>
>>> property().setter
<built-in method setter of property object at 0x10ff07940>
>>> property().deleter
<built-in method deleter of property object at 0x10ff07998>

Bunlar dekoratörler olarak da hareket eder . Yeni bir özellik nesnesi döndürürler:

>>> property().getter(None)
<property object at 0x10ff079f0>

eski nesnenin bir kopyasıdır, ancak işlevlerden biri değiştirilmiştir.

Unutmayın, @decoratorsözdizimi sadece sözdizimsel şeker; sözdizimi:

@property
def foo(self): return self._foo

gerçekten aynı şey demek

def foo(self): return self._foo
foo = property(foo)

böylece fooişlev, property(foo)yukarıda gördüğümüz özel bir nesnedir. Sonra kullandığınızda @foo.setter(), yaptığınız şey property().setter, size yukarıda gösterdiğim, özelliğin yeni bir kopyasını döndüren yöntemi çağırmaktır , ancak bu kez ayarlayıcı işleviyle dekore edilmiş yöntemle değiştirilir.

Aşağıdaki sıra, bu dekoratör yöntemlerini kullanarak tam özellikli bir özellik de oluşturur.

İlk propertyolarak sadece bir alıcı ile bazı fonksiyonlar ve bir nesne yaratırız :

>>> def getter(self): print('Get!')
... 
>>> def setter(self, value): print('Set to {!r}!'.format(value))
... 
>>> def deleter(self): print('Delete!')
... 
>>> prop = property(getter)
>>> prop.fget is getter
True
>>> prop.fset is None
True
>>> prop.fdel is None
True

Sonra .setter()bir ayarlayıcı eklemek için yöntemi kullanıyoruz :

>>> prop = prop.setter(setter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is None
True

Son olarak .deleter()yöntemle bir silici ekliyoruz :

>>> prop = prop.deleter(deleter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is deleter
True

Son fakat en az değil, propertynesne bir tanımlayıcı nesne gibi davranır , bu nedenle .__get__(), .__set__()ve .__delete__()alma, ayarlama ve silme örnek özniteliğine bağlanma yöntemleri vardır :

>>> class Foo: pass
... 
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!

Tanımlayıcı Howto , türün saf bir Python örnek uygulamasını içerir property():

class Property:
    "Emulate PyProperty_Type() in Objects/descrobject.c"

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)

    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError("can't set attribute")
        self.fset(obj, value)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError("can't delete attribute")
        self.fdel(obj)

    def getter(self, fget):
        return type(self)(fget, self.fset, self.fdel, self.__doc__)

    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__)

    def deleter(self, fdel):
        return type(self)(self.fget, self.fset, fdel, self.__doc__)

10
Çok iyi. İstediğiniz sonuç ile Foo.prop = propyapabileceğiniz gerçeğini ekleyebilirsiniz Foo().prop = 5; pront Foo().prop; del Foo().prop.
glglgl

12
Yöntem nesneleri anında oluşturulan ve edebilirsiniz varsa aynı bellek konumunu yeniden kullanın.
Martijn Pieters

1
@MarkusMeskanen: type()Dunder özelliklerine ve yöntemlerine erişim olarak kullanmayı tercih ediyorum , standart fonksiyonlar ve operatörler tarafından genişletme noktaları olarak kullanılacak.
Martijn Pieters

2
@MarkusMeskanen: çünkü nesne değişmez ve eğer onu mutasyona uğratırsanız, onu bir alt sınıfta uzmanlaştıramazsınız.
Martijn Pieters

5
@MarkusMeskanen: bkz. Ayarlayıcı olmadan Python geçersiz kılma alıcısı ; eğer @human.name.getterdeğiştirilmiş propertyyeni bir dönüş yerine yerinde nesnesi, human.namenitelik o üst sınıf davranışını değiştirerek, değişmiş olacaktır.
Martijn Pieters

201

Belgeler, salt okunur özellikler oluşturmak için sadece bir kısayol olduğunu söylüyor . Yani

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

eşittir

def getx(self):
    return self._x
x = property(getx)

19
Tam bağlam (en çok oylanan cevap) iyidir, ancak bu cevap, bir başkasının neden @propertysınıflarında dekoratör olarak kullandığını anlamak için pratik olarak kullanışlıdır .
ijoseph

1
@property, bir sınıfa öznitelik eklemek istediğinizde ve o sınıfın önceden oluşturulmuş nesnelerle uyumluluğu sürdürmeniz gerektiğinde de kullanılabilir (örneğin, bir turşu dosyasına kaydedilmiş olabilir).
AndyP

111

İşte nasıl @propertyuygulanabileceğine dair minimal bir örnek :

class Thing:
    def __init__(self, my_word):
        self._word = my_word 
    @property
    def word(self):
        return self._word

>>> print( Thing('ok').word )
'ok'

Aksi takdirde wordözellik yerine bir yöntem kalır.

class Thing:
    def __init__(self, my_word):
        self._word = my_word
    def word(self):
        return self._word

>>> print( Thing('ok').word() )
'ok'

1
Bu örnek, init içinde tanımlanması gereken word () işlevinin olup olmadığına nasıl bakar ?
JJ

5
Birisi lütfen neden bir özellik dekoratörü oluşturacağımı açıklayabilir mi, sadece sahip olmak yerine self.word = my_word- ki bu da aynı şekilde çalışırprint( Thing('ok').word ) = 'ok'
SilverSlash

1
@SilverSlash Bu sadece basit bir örnektir, gerçek bir kullanım durumu daha karmaşık bir yöntem içerecektir
AlexG

lütfen bana anlatabilir misiniz Thing('ok').word?
Vicrobot

83

İlk bölüm basit:

@property
def x(self): ...

aynıdır

def x(self): ...
x = property(x)
  • ki bu da, propertybir alıcıyla bir oluşturmak için basitleştirilmiş sözdizimidir .

Bir sonraki adım, bu özelliği bir ayarlayıcı ve silici ile genişletmek olacaktır. Ve bu uygun yöntemlerle olur:

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

her şeyi eski xartı verilen ayarlayıcıdan devralan yeni bir özellik döndürür .

x.deleter aynı şekilde çalışır.


49

Bu aşağıdaki:

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

Aynıdır:

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

    def _x_get(self):
        return self._x

    def _x_set(self, value):
        self._x = value

    def _x_del(self):
        del self._x

    x = property(_x_get, _x_set, _x_del, 
                    "I'm the 'x' property.")

Aynıdır:

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

    def _x_get(self):
        return self._x

    def _x_set(self, value):
        self._x = value

    def _x_del(self):
        del self._x

    x = property(_x_get, doc="I'm the 'x' property.")
    x = x.setter(_x_set)
    x = x.deleter(_x_del)

Aynıdır:

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

    def _x_get(self):
        return self._x
    x = property(_x_get, doc="I'm the 'x' property.")

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

    def _x_del(self):
        del self._x
    x = x.deleter(_x_del)

Hangisi ile aynı:

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

4
İlk ve son kod örnekleri aynıdır (kelimesi kelimesine).
Adomas Baliuka

47

Aşağıda bir buradan@property alınan kodu refactor gerektiğinde nasıl yardımcı olabilir başka bir örnek (Ben sadece aşağıda özetlemektedir):

Şöyle bir sınıf oluşturduğunuzu düşünün Money:

class Money:
    def __init__(self, dollars, cents):
        self.dollars = dollars
        self.cents = cents

ve kullanıcı kullandığı bu sınıfa bağlı olarak bir kütüphane oluşturur.

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.

Şimdi diyelim ki Moneysınıfınızı değiştirmeye dollarsve centsözelliklerinden kurtulmaya karar verdiniz , bunun yerine yalnızca toplam sent miktarını izlemeye karar verin:

class Money:
    def __init__(self, dollars, cents):
        self.total_cents = dollars * 100 + cents

Yukarıda belirtilen kullanıcı şimdi daha önce olduğu gibi kütüphanesini çalıştırmaya çalışırsa

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))

bir hataya neden olur

AttributeError: 'Para' nesnesinin 'dolar' özelliği yok

Şimdi, orijinal dayanır herkesin aracı Yani Moneysınıfın tüm kod satırlarını değiştirmek zorunda kalacak dollarsve centsçok acı verici olabilir kullanılmaktadır ... Yani, bu nasıl önlenebilir? Kullanarak@property !

Bu şekilde:

class Money:
    def __init__(self, dollars, cents):
        self.total_cents = dollars * 100 + cents

    # Getter and setter for dollars...
    @property
    def dollars(self):
        return self.total_cents // 100

    @dollars.setter
    def dollars(self, new_dollars):
        self.total_cents = 100 * new_dollars + self.cents

    # And the getter and setter for cents.
    @property
    def cents(self):
        return self.total_cents % 100

    @cents.setter
    def cents(self, new_cents):
        self.total_cents = 100 * self.dollars + new_cents

şimdi kütüphanemizden aradığımızda

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.

beklendiği gibi çalışacak ve kütüphanemizdeki tek bir kod satırını değiştirmek zorunda kalmadık! Aslında, bağımlı olduğumuz kütüphanenin değiştiğini bile bilmek zorunda değiliz.

Ayrıca setteriyi çalışır:

money.dollars += 2
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 12 cents.

money.cents += 10
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 22 cents.

@propertySoyut sınıflarda da kullanabilirsiniz ; Burada minimal bir örnek vereceğim .


özetiniz çok iyi, web sitesinin aldığı örnek biraz garip .. Bir acemi sorardı .. neden sadece yapamıyoruz self.dollar = dollars? @property ile çok şey yaptık, ancak hiçbir eklenti işlevi eklenmedi gibi görünüyor.
Sheng Bi

1
@ShengBi: Bu örneğe o kadar çok odaklanmayın, temel prensibe daha fazla odaklanın: Eğer - herhangi bir nedenle - kodu yeniden düzenlemek zorunda kalırsanız, bunu başkalarının kodunu etkilemeden yapabilirsiniz.
Cleb

21

Buradaki tüm yazıları okudum ve gerçek bir yaşam örneğine ihtiyacımız olabileceğini fark ettim. Neden @ mülkiyetimiz var? Bu nedenle, kimlik doğrulama sistemini kullandığınız bir Flask uygulamasını düşünün. Model Kullanıcıyı şu adreste beyan edersiniz models.py:

class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(64), unique=True, index=True)
    username = db.Column(db.String(64), unique=True, index=True)
    password_hash = db.Column(db.String(128))

    ...

    @property
    def password(self):
        raise AttributeError('password is not a readable attribute')

    @password.setter
    def password(self, password):
        self.password_hash = generate_password_hash(password)

    def verify_password(self, password):
        return check_password_hash(self.password_hash, password)

Bu kodda , gerçek örnek değişkenini ayarlamak için @ property.setter kullandığımızda, doğrudan erişmeye çalıştığınızda onaylamayı tetikleyen tetikleme özelliğini passwordkullanarak "gizli" özelliğimiz var .@propertyAttributeErrorpassword_hash .

Şimdi auth/views.pybir Kullanıcıyı aşağıdakilerle başlatabiliriz:

...
@auth.route('/register', methods=['GET', 'POST'])
def register():
    form = RegisterForm()
    if form.validate_on_submit():
        user = User(email=form.email.data,
                    username=form.username.data,
                    password=form.password.data)
        db.session.add(user)
        db.session.commit()
...

passwordBir kullanıcı formu doldurduğunda bir kayıt formundan gelen dikkat özniteliği . Şifre onayı kullanıcı arabirimindeEqualTo('password', message='Passwords must match') (merak ediyorsanız, ancak Flask formlarıyla ilgili farklı bir konu olması durumunda) ile gerçekleşir.

Umarım bu örnek faydalı olacaktır


18

Bu nokta orada birçok insan tarafından temizlendi, ancak işte aradığım doğrudan bir nokta. @Property dekoratör ile başlamak önemli olduğunu düşünüyorum. Örneğin:-

class UtilityMixin():
    @property
    def get_config(self):
        return "This is property"

"Get_config ()" işlevinin çağrılması bu şekilde çalışır.

util = UtilityMixin()
print(util.get_config)

Fark ederseniz, işlevi çağırmak için "()" köşeli parantez kullanmadığımı fark ederseniz. @Property dekoratör için aradığım temel şey bu. Böylece işlevinizi bir değişken gibi kullanabilirsiniz.


1
Bu soyut kavramın yoğunlaşmasına yardımcı olan çok kullanışlı bir nokta.
Info5ek

18

Python dekoratörleri ile başlayalım.

Python dekoratörü, önceden tanımlanmış bir işleve bazı ek işlevler eklemeye yardımcı olan bir işlevdir.

Python'da her şey bir nesnedir. Python'daki işlevler birinci sınıf nesnelerdir, yani listelere eklenen, başka bir işleve argüman olarak iletilen bir değişken tarafından başvurulabilir.

Aşağıdaki kod snippet'ini düşünün.

def decorator_func(fun):
    def wrapper_func():
        print("Wrapper function started")
        fun()
        print("Given function decorated")
        # Wrapper function add something to the passed function and decorator 
        # returns the wrapper function
    return wrapper_func

def say_bye():
    print("bye!!")

say_bye = decorator_func(say_bye)
say_bye()

# Output:
#  Wrapper function started
#  bye
#  Given function decorated

Burada, dekoratör işlevinin say_hello işlevimizi değiştirdiğini ve içine bazı ekstra kod satırları eklediğini söyleyebiliriz.

Dekoratör için Python sözdizimi

def decorator_func(fun):
    def wrapper_func():
        print("Wrapper function started")
        fun()
        print("Given function decorated")
        # Wrapper function add something to the passed function and decorator 
        # returns the wrapper function
    return wrapper_func

@decorator_func
def say_bye():
    print("bye!!")

say_bye()

Her şeyi bir senaryodan ziyade sonuçlandıralım, ama bundan önce bazı ayıp prensipleri hakkında konuşalım.

Harfler ve ayarlayıcılar, veri kapsülleme ilkesini sağlamak için birçok nesne yönelimli programlama dilinde kullanılır (verilerin bu veriler üzerinde çalışan yöntemlerle birleştirilmesi olarak görülür).

Bu yöntemler elbette verileri almak için alıcı ve verileri değiştirmek için ayarlayıcıdır.

Bu ilkeye göre, bir sınıfın nitelikleri, onları diğer kodlardan gizlemek ve korumak için özel yapılır.

Evet, @property temel olarak alıcıları ve ayarlayıcıları kullanmanın pythonic bir yoludur.

Python, nesneye yönelik bir programlayıcının hayatını çok daha basit hale getiren özellik adı verilen harika bir konsepte sahiptir.

Sıcaklığı Celsius derecesinde depolayabilecek bir sınıf yapmaya karar verdiğinizi varsayalım.

class Celsius:
def __init__(self, temperature = 0):
    self.set_temperature(temperature)

def to_fahrenheit(self):
    return (self.get_temperature() * 1.8) + 32

def get_temperature(self):
    return self._temperature

def set_temperature(self, value):
    if value < -273:
        raise ValueError("Temperature below -273 is not possible")
    self._temperature = value

Refactored Code, İşte bunu mülkle nasıl başarabiliriz.

Python'da property (), bir özellik nesnesi oluşturan ve döndüren yerleşik bir işlevdir.

Bir özellik nesnesinin getter (), setter () ve delete () olmak üzere üç yöntemi vardır.

class Celsius:
def __init__(self, temperature = 0):
    self.temperature = temperature

def to_fahrenheit(self):
    return (self.temperature * 1.8) + 32

def get_temperature(self):
    print("Getting value")
    return self.temperature

def set_temperature(self, value):
    if value < -273:
        raise ValueError("Temperature below -273 is not possible")
    print("Setting value")
    self.temperature = value

temperature = property(get_temperature,set_temperature)

Buraya,

temperature = property(get_temperature,set_temperature)

olarak parçalanmış olabilir,

# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)

Dikkat Edilmesi Gereken Noktalar:

  • get_temperature yöntem yerine bir özellik olarak kalır.

Artık sıcaklığın değerine yazarak erişebilirsiniz.

C = Celsius()
C.temperature
# instead of writing C.get_temperature()

Daha da devam edebilir ve get_temperature ve set_temperature adlarını gereksiz olduklarından tanımlayabilir ve sınıf ad alanını kirletebiliriz.

Pythonic yolu yukarıdaki sorunla başa çıkmak için kullanmaktır @property .

class Celsius:
    def __init__(self, temperature = 0):
        self.temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

    @property
    def temperature(self):
        print("Getting value")
        return self.temperature

    @temperature.setter
    def temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        print("Setting value")
        self.temperature = value

Dikkat Edilmesi Gereken Noktalar -

  1. Bir değer elde etmek için kullanılan bir yöntem "@property" ile süslenmiştir.
  2. Ayarlayıcı olarak işlev görmesi gereken yöntem "@ temperature.setter" ile dekore edilmiştir. Eğer fonksiyon "x" olarak adlandırılmış olsaydı, bunu "@ x.setter" ile süslememiz gerekirdi.
  3. Aynı ada ve farklı sayıda "def temperature (self)" ve "def temperature (self, x)" parametrelerine sahip "iki" yöntem yazdık.

Gördüğünüz gibi, kod kesinlikle daha az zarif.

Şimdi, gerçek bir pratik senaryo hakkında konuşalım.

Diyelim ki aşağıdaki gibi bir sınıf tasarladınız:

class OurClass:

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


y = OurClass(10)
print(y.x)

Şimdi, sınıfımızın müşteriler arasında popüler olduğunu ve programlarında kullanmaya başladığını varsayalım, nesneye her türlü görevi yaptılar.

Ve bir kader gün, güvenilir bir müşteri bize geldi ve "x" değerinin 0 ile 1000 arasında bir değer olması gerektiğini önerdi, bu gerçekten korkunç bir senaryo!

Özellikleri nedeniyle kolaydır: "x" özellik sürümünü oluştururuz.

class OurClass:

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

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

    @x.setter
    def x(self, x):
        if x < 0:
            self.__x = 0
        elif x > 1000:
            self.__x = 1000
        else:
            self.__x = x

Bu harika, değil mi: Akla gelebilecek en basit uygulama ile başlayabilirsiniz ve daha sonra arayüzü değiştirmek zorunda kalmadan bir mülk sürümüne geçiş yapmakta özgürsünüz! Yani özellikler sadece alıcılar ve ayarlayıcıların yerini almaz!

Bu Uygulamayı buradan kontrol edebilirsiniz


2
Celsius sınıfınız ayar yaparken sonsuz örnek alır (bu örnekleme anlamına gelir).
Ted Petrou

1
@Ted Petrou Seni anlamadım mı? Ayarlama sırasında sonsuz miktarda nasıl tekrarlanır?
Divyanshu Rawat

Bu aslında belli değil ... insanlar nedenini soruyor, ama örnek ikna edici değil ...
Sheng Bi

1
Bu sadece bir yorum, benim kişisel görüşüm. Cevabınız gerçekten iyi olabilir. bu yüzden bırakın.
Sheng Bi

1
en çok oy alan cevaplara kıyasla, bu cevap insanlar için tasarlanmıştır; Teşekkürler.
Info5ek

6

property@propertydekoratörün arkasında bir sınıftır .

Bunu her zaman kontrol edebilirsiniz:

print(property) #<class 'property'>

Ben den örnek yeniden yazdım help(property)o programı @propertysözdizimi

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

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

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

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

c = C()
c.x="a"
print(c.x)

property()sözdizimi ile işlevsel olarak aynıdır :

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

    def g(self):
        return self._x

    def s(self, v):
        self._x = v

    def d(self):
        del self._x

    prop = property(g,s,d)

c = C()
c.x="a"
print(c.x)

Gördüğünüz gibi mülkü nasıl kullandığımız konusunda bir fark yok.

Soruyu cevaplamak için @propertydekoratör propertysınıf yoluyla uygulanır .


Yani, soru propertysınıfı biraz açıklamaktır . Bu hat:

prop = property(g,s,d)

Başlatma oldu. Bunu şu şekilde yeniden yazabiliriz:

prop = property(fget=g,fset=s,fdel=d)

Arasında anlam fget, fsetve fdel:

 |    fget
 |      function to be used for getting an attribute value
 |    fset
 |      function to be used for setting an attribute value
 |    fdel
 |      function to be used for del'ing an attribute
 |    doc
 |      docstring

Bir sonraki resim, sınıfımızdaki üçlüleri göstermektedir property:

resim açıklamasını buraya girin

__get__, __set__ve geçersiz__delete__ kılınacaklar mı ? Bu, Python'daki tanımlayıcı modelin uygulanmasıdır.

Genel olarak, bir tanımlayıcı, tanımlayıcı protokolündeki yöntemlerle öznitelik erişimi geçersiz kılınmış olan “bağlama davranışı” olan bir nesne özniteliğidir.

Ayrıca özelliğini kullanabilir setter, getterve deleteryöntemler özelliğine işlevini bağlamak. Bir sonraki örneği kontrol edin. s2Sınıfın yöntemi Cözelliği iki katına çıkarır .

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

    def g(self):
        return self._x

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

    def d(self):
        del self._x

    def s2(self,x):
        self._x=x+x


    x=property(g)
    x=x.setter(s)
    x=x.deleter(d)      


c = C()
c.x="a"
print(c.x) # outputs "a"

C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x) # outputs "aa"

1

Bir mülk iki şekilde beyan edilebilir.

  • Bir öznitelik için getter, setter yöntemleri oluşturma ve bunları özellik işlevine argüman olarak geçirme
  • @Property dekoratörünü kullanma .

Python'daki özellikler hakkında yazdığım birkaç örneğe bakabilirsiniz .


cevabınızı güncelleyebilir miyim, mülkün bir sınıf olduğunu, bu yüzden ben oylayabilirim.
prosti


0

İşte başka bir örnek:

##
## Python Properties Example
##
class GetterSetterExample( object ):
    ## Set the default value for x ( we reference it using self.x, set a value using self.x = value )
    __x = None


##
## On Class Initialization - do something... if we want..
##
def __init__( self ):
    ## Set a value to __x through the getter / setter... Since __x is defined above, this doesn't need to be set...
    self.x = 1234

    return None


##
## Define x as a property, ie a getter - All getters should have a default value arg, so I added it - it will not be passed in when setting a value, so you need to set the default here so it will be used..
##
@property
def x( self, _default = None ):
    ## I added an optional default value argument as all getters should have this - set it to the default value you want to return...
    _value = ( self.__x, _default )[ self.__x == None ]

    ## Debugging - so you can see the order the calls are made...
    print( '[ Test Class ] Get x = ' + str( _value ) )

    ## Return the value - we are a getter afterall...
    return _value


##
## Define the setter function for x...
##
@x.setter
def x( self, _value = None ):
    ## Debugging - so you can see the order the calls are made...
    print( '[ Test Class ] Set x = ' + str( _value ) )

    ## This is to show the setter function works.... If the value is above 0, set it to a negative value... otherwise keep it as is ( 0 is the only non-negative number, it can't be negative or positive anyway )
    if ( _value > 0 ):
        self.__x = -_value
    else:
        self.__x = _value


##
## Define the deleter function for x...
##
@x.deleter
def x( self ):
    ## Unload the assignment / data for x
    if ( self.__x != None ):
        del self.__x


##
## To String / Output Function for the class - this will show the property value for each property we add...
##
def __str__( self ):
    ## Output the x property data...
    print( '[ x ] ' + str( self.x ) )


    ## Return a new line - technically we should return a string so it can be printed where we want it, instead of printed early if _data = str( C( ) ) is used....
    return '\n'

##
##
##
_test = GetterSetterExample( )
print( _test )

## For some reason the deleter isn't being called...
del _test.x

Temel olarak, bunun yerine x kullanıyorum dışında C (nesne) örneğiyle aynı ... Ben de __init - ... de .. de başlatmıyorum. sınıfın....

Çıktı:

[ Test Class ] Set x = 1234
[ Test Class ] Get x = -1234
[ x ] -1234

ve init içinde self.x = 1234 yorum yaparsam çıktı:

[ Test Class ] Get x = None
[ x ] None

ve getter işlevinde _default = None değerini _default = 0 olarak ayarlarsam (tüm alıcılar varsayılan bir değere sahip olmalı, ancak gördüğüm özellik özelliklerinden geçmediği için burada tanımlayabilirsiniz ve aslında kötü değil çünkü varsayılanı bir kez tanımlayabilir ve her yerde kullanabilirsiniz) ie: def x (self, _default = 0):

[ Test Class ] Get x = 0
[ x ] 0

Not: Alıcı mantığı, değerin onun tarafından manipüle edildiğinden emin olmak için değiştirilmesini sağlamak için oradadır - baskı ifadeleri için aynıdır ...

Not: Lua'ya alışkınım ve tek bir işlevi çağırdığımda 10+ yardımcıyı dinamik olarak oluşturabiliyorum ve özellikleri kullanmadan Python için benzer bir şey yaptım ve bir dereceye kadar çalışıyor, ancak işlevler daha önce oluşturulsa bile Kullanılmadan önce, bu şekilde kodlanmadığı için garip olan çağrılmadan önce hala sorunlar var ... Lua meta tablolarının esnekliğini ve gerçek ayarlayıcıları / alıcıları kullanabileceğimi tercih ediyorum aslında bir değişkene doğrudan erişmek yerine ... Python ile bazı şeylerin ne kadar çabuk oluşturulabileceğini seviyorum - örneğin gui programları. Ben bir tasarım ek kütüphaneler olmadan mümkün olmayabilir rağmen - AutoHotkey kod ben doğrudan dll çağrılarıma erişebilir ve aynı Java, C #, C ++, yapılabilir,

Not: Bu forumdaki kod çıktısı bozuldu - Çalışması için kodun ilk kısmına boşluklar eklemek zorunda kaldım - kopyalama / yapıştırma, tüm boşlukları sekmelere dönüştürdüğünüzde .... Python için sekmeler kullanıyorum çünkü dosya boyutu 10.000 satır olan dosya boyutu 512KB ile 1MB arasında boşluklar ve 100 ila 200KB arasında sekmelerle dosya boyutu için büyük bir fark ve işlem süresinde azalma olabilir ...

Sekmeler kullanıcı başına da ayarlanabilir - bu nedenle 2 boşluk genişliği, 4, 8 veya yapabileceğiniz her şeyi tercih ederseniz, bu, görme yeteneği eksikliği olan geliştiriciler için düşünceli olduğu anlamına gelir.

Not: Sınıf yazılımında bir hata nedeniyle sınıfta tanımlanan tüm işlevler düzgün girintili değildir - kopyalayıp yapıştırdığınızda girintili olduğundan emin olun


-3

Bir açıklama: benim için, Python 2.x için, @propertydevralmadığımda reklamı yapılan gibi çalışmadı object:

class A():
    pass

ancak şu durumlarda çalıştı:

class A(object):
    pass

Python 3 için her zaman çalıştı.


5
Çünkü Python 2'de, miras alınmayan bir sınıf objecteski tarz bir sınıftır ve eski tarz sınıflar tanımlayıcı protokolünü desteklemez (ki propertybu şekilde çalışmak için uygular). Python 3'te eski tarz sınıflar artık mevcut değil; tüm sınıflar Python 2'de yeni stil sınıfları olarak adlandırdığımız
şeydir
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.