Bagaimana cara kerja dekorator properti?

981

Saya ingin memahami cara propertykerja fungsi bawaan. Yang membingungkan saya adalah bahwa itu propertyjuga dapat digunakan sebagai dekorator, tetapi hanya membutuhkan argumen ketika digunakan sebagai fungsi bawaan dan bukan saat digunakan sebagai dekorator.

Contoh ini dari dokumentasi :

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

propertyargumen 's adalah getx, setx, delxdan string doc.

Dalam kode di bawah propertyini digunakan sebagai dekorator. Objek itu adalah xfungsi, tetapi dalam kode di atas tidak ada tempat untuk fungsi objek dalam argumen.

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

Dan, bagaimana dengan x.setterdan x.deleterdekorator dibuat? Saya bingung.

ashim
sumber
13
Lihat juga: Bagaimana cara kerja properti Python?
Martin Thoma
3
propertysebenarnya adalah sebuah kelas (bukan fungsi), meskipun mungkin tidak memanggil __init__()metode ketika Anda membuat objek, tentu saja. Menggunakan help(property)dari terminal itu berwawasan luas. helpjuga kelas karena beberapa alasan.
Brōtsyorfuzthrāx
Saya pikir tautan ini memberikan contoh yang baik: [properti] ( journaldev.com/14893/python-property-decorator )
Sheng Bi
4
@Shule utas 2 tahun, tapi tetap saja: Semuanya kelas. Bahkan kelas.
Artemis masih tidak percaya
2
Ini membingungkan saya juga. Saya akhirnya menemukan artikel yang dapat memecahnya untuk saya. Saya harap ini membantu orang lain. programiz.com/python-programming/property Saya tidak berafiliasi dengan cara apa pun dengan situs ini.
jjwdesign

Jawaban:

1010

The property()mengembalikan fungsi khusus objek descriptor :

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

Objek inilah yang memiliki metode tambahan :

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

Ini bertindak sebagai dekorator juga . Mereka mengembalikan objek properti baru:

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

itu adalah salinan dari objek lama, tetapi dengan salah satu fungsi diganti.

Ingat, bahwa @decoratorsintaksis hanyalah gula sintaksis; sintaks:

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

benar-benar berarti sama dengan

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

jadi foofungsinya diganti oleh property(foo), yang kita lihat di atas adalah objek khusus. Kemudian ketika Anda menggunakan @foo.setter(), apa yang Anda lakukan adalah menyebutnya demikianproperty().setter metode yang saya tunjukkan di atas, yang mengembalikan salinan properti baru, tetapi kali ini dengan fungsi setter diganti dengan metode yang didekorasi.

Urutan berikut juga menciptakan properti penuh, dengan menggunakan metode dekorator tersebut.

Pertama kita membuat beberapa fungsi dan propertyobjek hanya dengan pengambil:

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

Selanjutnya kita menggunakan .setter()metode untuk menambahkan setter:

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

Terakhir kita tambahkan deleter dengan .deleter()metode:

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

Last but not least, propertyobjek tersebut bertindak sebagai objek deskriptor , sehingga memiliki .__get__(), .__set__()dan .__delete__()metode untuk menghubungkan ke atribut instance get, setting, dan delete:

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

The Descriptor Howto menyertakan implementasi sampel Python murni dari property()tipe:

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__)
Martijn Pieters
sumber
10
Baik sekali. Anda bisa menambahkan fakta bahwa setelah Foo.prop = propAnda bisa lakukan Foo().prop = 5; pront Foo().prop; del Foo().propdengan hasil yang diinginkan.
glglgl
12
Metode objek dibuat dengan cepat dan dapat menggunakan kembali lokasi memori yang sama jika tersedia.
Martijn Pieters
1
@MarkusMeskanen: Saya lebih suka menggunakan type()mengakses atribut dan metode dunder dimaksudkan untuk digunakan sebagai titik ekstensi oleh fungsi standar dan operator.
Martijn Pieters
2
@MarkusMeskanen: karena objek tidak dapat diubah, dan jika Anda memutasikannya di tempat Anda tidak dapat mengkhususkannya dalam subkelas.
Martijn Pieters
5
@MarkusMeskanen: lihat pengambil getar utama Python tanpa setter ; jika @human.name.gettermengubah propertyobjek di tempat alih-alih mengembalikan yang baru, human.nameatribut akan diubah, mengubah perilaku superclass itu.
Martijn Pieters
201

Dokumentasi mengatakan itu hanya jalan pintas untuk membuat properti readonly. Begitu

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

setara dengan

def getx(self):
    return self._x
x = property(getx)
J0HN
sumber
19
Konteks penuh (jawaban yang paling banyak dipilih) bagus, tetapi jawaban ini praktis berguna untuk mencari tahu mengapa orang lain menggunakan @propertysebagai dekorator di kelas mereka.
ijoseph
1
@ properti juga dapat digunakan ketika Anda ingin menambahkan atribut ke kelas dan perlu mempertahankan kompatibilitas dengan objek yang dibuat sebelumnya dari kelas itu (misalnya yang mungkin disimpan dalam file acar).
AndyP
112

Berikut adalah contoh minimal tentang bagaimana @propertydapat diterapkan:

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

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

Kalau tidak, wordtetap metode bukan properti.

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

>>> print( Thing('ok').word() )
'ok'
AlexG
sumber
1
Bagaimana contoh ini terlihat jika kata () fungsi / properti perlu didefinisikan di init ?
JJ
5
Dapatkah seseorang tolong jelaskan mengapa saya akan membuat dekorator properti di sini, daripada hanya memiliki self.word = my_word- yang kemudian akan bekerja dengan cara yang samaprint( Thing('ok').word ) = 'ok'
SilverSlash
1
@SilverSlash Ini hanya contoh sederhana, kasus penggunaan nyata akan melibatkan metode yang lebih rumit
AlexG
bisa tolong jelaskan padaku, bagaimana cara mencetak Thing('ok').wordmemanggil fungsi secara internal saat runtime?
Vicrobot
83

Bagian pertama adalah sederhana:

@property
def x(self): ...

sama dengan

def x(self): ...
x = property(x)
  • yang, pada gilirannya, adalah sintaks yang disederhanakan untuk membuat propertyhanya dengan pengambil.

Langkah selanjutnya adalah memperluas properti ini dengan setter dan deleter. Dan ini terjadi dengan metode yang sesuai:

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

mengembalikan properti baru yang mewarisi semuanya dari yang lama xplus setter yang diberikan.

x.deleter bekerja dengan cara yang sama.

glglgl
sumber
49

Berikut ini:

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

Sama dengan:

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

Sama dengan:

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)

Sama dengan:

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)

Yang sama dengan:

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
Bill Moore
sumber
4
Contoh kode pertama dan terakhir adalah sama (kata demi kata).
Adomas Baliuka
47

Di bawah ini adalah contoh lain tentang bagaimana @propertybisa membantu ketika seseorang harus memperbaiki kode yang diambil dari sini (saya hanya meringkasnya di bawah):

Bayangkan Anda membuat kelas Moneyseperti ini:

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

dan seorang pengguna membuat perpustakaan tergantung pada kelas ini di mana ia menggunakan mis

money = Money(27, 12)

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

Sekarang anggaplah Anda memutuskan untuk mengubah Moneykelas Anda dan menyingkirkan atribut dollarsdan centstetapi memutuskan untuk hanya melacak jumlah total sen:

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

Jika pengguna yang disebutkan di atas sekarang mencoba untuk menjalankan perpustakaannya seperti sebelumnya

money = Money(27, 12)

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

itu akan menghasilkan kesalahan

AttributeError: Objek 'Uang' tidak memiliki atribut 'dolar'

Itu berarti bahwa sekarang semua orang yang bergantung pada Moneykelas asli Anda harus mengubah semua baris kode di mana dollarsdan centsdigunakan yang bisa sangat menyakitkan ... Jadi, bagaimana ini bisa dihindari? Dengan menggunakan@property !

Begitulah caranya:

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

ketika kami sekarang menelepon dari perpustakaan kami

money = Money(27, 12)

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

ini akan berfungsi seperti yang diharapkan dan kami tidak perlu mengubah satu baris kode pun di perpustakaan kami! Bahkan, kita bahkan tidak perlu tahu bahwa perpustakaan tempat kita bergantung berubah.

Juga setterberfungsi dengan baik:

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.

Anda dapat menggunakan @propertyjuga dalam kelas abstrak; Saya berikan contoh minimal di sini .

Cleb
sumber
ringkasan Anda sangat bagus, contoh yang diambil situs web sedikit aneh .. Seorang pemula akan bertanya .. mengapa kita tidak bisa tetap berpegang pada self.dollar = dollars? kami telah melakukan banyak hal dengan @property, tetapi sepertinya tidak ada fungsionalitas ekstrak yang ditambahkan.
Sheng Bi
1
@ ShengBi: Jangan terlalu fokus pada contoh aktual tetapi lebih pada prinsip dasar: Jika - untuk alasan apa pun - Anda harus memperbaiki kode, Anda dapat melakukannya tanpa memengaruhi kode orang lain.
Cleb
21

Saya membaca semua posting di sini dan menyadari bahwa kita mungkin membutuhkan contoh kehidupan nyata. Kenapa, sebenarnya, kita punya @ properti? Jadi, pertimbangkan aplikasi Flask tempat Anda menggunakan sistem otentikasi. Anda mendeklarasikan Pengguna model di 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)

Dalam kode ini kita telah atribut "tersembunyi" passworddengan menggunakan @propertyyang memicu AttributeErrorpernyataan ketika Anda mencoba mengaksesnya secara langsung, sementara kami menggunakan @ property.setter untuk mengatur variabel instance aktual password_hash.

Sekarang auth/views.pykita dapat instantiate Pengguna dengan:

...
@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()
...

Atribut pemberitahuan passwordyang berasal dari formulir pendaftaran ketika pengguna mengisi formulir. Konfirmasi kata sandi terjadi di ujung depan dengan EqualTo('password', message='Passwords must match')(jika Anda bertanya-tanya, tapi itu topik yang berbeda terkait bentuk Flask).

Semoga contoh ini bermanfaat

Leo Skhrnkv
sumber
18

Titik ini telah dibersihkan oleh banyak orang di sana tetapi di sini ada titik langsung yang saya cari. Inilah yang saya rasa penting untuk memulai dengan dekorator @ properti. misalnya:-

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

Pemanggilan fungsi "get_config ()" akan berfungsi seperti ini.

util = UtilityMixin()
print(util.get_config)

Jika Anda perhatikan saya belum menggunakan tanda kurung "()" untuk memanggil fungsi. Ini adalah hal dasar yang saya cari pada dekorator @property. Sehingga Anda dapat menggunakan fungsi Anda seperti variabel.

Devendra Bhat
sumber
1
Poin yang sangat berguna yang membantu menyingkat konsep abstrak ini.
Info5ek
18

Mari kita mulai dengan dekorator Python.

Dekorator Python adalah fungsi yang membantu menambahkan beberapa fungsi tambahan ke fungsi yang sudah didefinisikan.

Dalam Python, semuanya adalah objek. Fungsi dalam Python adalah objek kelas satu yang berarti bahwa mereka dapat direferensikan oleh variabel, ditambahkan dalam daftar, diteruskan sebagai argumen ke fungsi lain, dll.

Pertimbangkan potongan kode berikut.

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

Di sini, kita dapat mengatakan bahwa fungsi dekorator memodifikasi fungsi say_hello kita dan menambahkan beberapa baris kode tambahan di dalamnya.

Sintaksis python untuk dekorator

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

Mari kita simpulkan semuanya dari pada dengan skenario kasus, tapi sebelum itu mari kita bicara tentang beberapa oop yang berpartisipasi.

Getters dan setter digunakan dalam banyak bahasa pemrograman berorientasi objek untuk memastikan prinsip enkapsulasi data (dipandang sebagai bundling data dengan metode yang beroperasi pada data ini.)

Metode-metode ini tentu saja pengambil untuk mengambil data dan setter untuk mengubah data.

Menurut prinsip ini, atribut kelas dibuat pribadi untuk menyembunyikan dan melindunginya dari kode lain.

Yup, @property pada dasarnya adalah cara pythonic untuk menggunakan getter dan setter.

Python memiliki konsep hebat yang disebut properti yang membuat kehidupan seorang programmer berorientasi objek jauh lebih sederhana.

Mari kita asumsikan bahwa Anda memutuskan untuk membuat kelas yang dapat menyimpan suhu dalam derajat Celcius.

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

Kode Refactored, Inilah cara kami dapat mencapainya dengan properti.

Dalam Python, properti () adalah fungsi bawaan yang membuat dan mengembalikan objek properti.

Objek properti memiliki tiga metode, pengambil (), setter (), dan hapus ().

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)

Sini,

temperature = property(get_temperature,set_temperature)

bisa dipecah sebagai,

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

Catatan:

  • get_temperature tetap menjadi properti alih-alih metode.

Sekarang Anda dapat mengakses nilai suhu dengan menulis.

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

Kita dapat melanjutkan dan tidak mendefinisikan nama get_temperature dan set_temperature karena mereka tidak perlu dan mencemari namespace kelas.

Cara pythonic untuk mengatasi masalah di atas adalah dengan menggunakan @ properti .

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

Poin yang Perlu Dicatat -

  1. Metode yang digunakan untuk mendapatkan nilai didekorasi dengan "@ properti".
  2. Metode yang berfungsi sebagai setter didekorasi dengan "@ temperature.setter", Jika fungsi itu disebut "x", kita harus menghiasnya dengan "@ x.setter".
  3. Kami menulis "dua" metode dengan nama yang sama dan jumlah parameter yang berbeda "suhu def (self)" dan "suhu def (self, x)".

Seperti yang Anda lihat, kode ini jelas kurang elegan.

Sekarang, mari kita bicara tentang satu scenerio praktis kehidupan nyata.

Katakanlah Anda telah merancang kelas sebagai berikut:

class OurClass:

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


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

Sekarang, mari kita asumsikan bahwa kelas kita menjadi populer di kalangan klien dan mereka mulai menggunakannya dalam program mereka, mereka melakukan semua jenis tugas pada objek.

Dan Suatu hari yang menentukan, klien tepercaya datang kepada kami dan menyarankan bahwa "x" harus bernilai antara 0 dan 1000, ini benar-benar skenario yang mengerikan!

Karena properti itu mudah: Kami membuat versi properti "x".

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

Ini bagus, bukan: Anda bisa mulai dengan implementasi paling sederhana yang bisa dibayangkan, dan Anda bebas untuk bermigrasi ke versi properti tanpa harus mengubah antarmuka! Jadi properti bukan hanya pengganti untuk pengambil dan penyetel!

Anda dapat memeriksa Implementasi ini di sini

Divyanshu Rawat
sumber
2
Kelas Celsius Anda akan berulang berulang saat pengaturan (yang berarti pada instantiasi).
Ted Petrou
1
@Ted Petrou I't get you? Bagaimana itu akan berulang secara tak terbatas saat pengaturan?
Divyanshu Rawat
Ini sebenarnya tidak jelas ... orang-orang bertanya mengapa, tetapi contohnya tidak meyakinkan ...
Sheng Bi
1
Itu hanya komentar, pendapat pribadi saya. Jawaban Anda bisa sangat bagus. jadi tinggalkan saja.
Sheng Bi
1
dibandingkan dengan jawaban pilihan teratas, ini dirancang untuk manusia; Terima kasih.
Info5ek
6

propertyadalah kelas di belakang @propertydekorator.

Anda selalu dapat memeriksa ini:

print(property) #<class 'property'>

Saya menulis ulang contoh dari help(property)untuk menunjukkan bahwa @propertysintaks

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)

secara fungsional identik dengan property()sintaks:

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)

Tidak ada perbedaan bagaimana kami menggunakan properti seperti yang Anda lihat.

Untuk menjawab pertanyaan @propertydekorator dilaksanakan melalui propertykelas.


Jadi, pertanyaannya adalah untuk menjelaskan propertykelas sedikit. Garis ini:

prop = property(g,s,d)

Adalah inisialisasi. Kami dapat menulis ulang seperti ini:

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

Arti fget, fsetdan 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

Gambar selanjutnya menunjukkan kembar tiga yang kita miliki, dari kelas property:

masukkan deskripsi gambar di sini

__get__,, __set__dan __delete__ada yang akan ditimpa . Ini adalah implementasi dari pola deskriptor dengan Python.

Secara umum, deskriptor adalah atribut objek dengan "perilaku mengikat", atribut yang akses atributnya telah ditimpa oleh metode dalam protokol deskriptor.

Kita juga bisa menggunakan properti setter, getterdan deletermetode untuk mengikat fungsi ke properti. Lihat contoh berikut. Metode s2kelas Cakan mengatur properti menjadi dua kali lipat .

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"
prosti
sumber
1

Properti dapat dideklarasikan dalam dua cara.

  • Membuat pengambil, metode penyetel untuk atribut, lalu meneruskannya sebagai argumen ke fungsi properti
  • Menggunakan dekorator properti @ .

Anda dapat melihat beberapa contoh yang saya tulis tentang properti dengan python .

nvd
sumber
dapatkah Anda memperbarui jawaban Anda dengan mengatakan bahwa properti adalah kelas sehingga saya dapat memperbaiki.
prosti
1

Penjelasan terbaik dapat ditemukan di sini: Dijelaskan Python @Property - Bagaimana Cara Menggunakan dan Kapan? (Contoh Lengkap) oleh Selva Prabhakaran | Diposting pada 5 November 2018

Itu membantu saya memahami MENGAPA tidak hanya BAGAIMANA.

https://www.machinelearningplus.com/python/python-property/

Victor Wang
sumber
0

Ini adalah contoh lain:

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

Pada dasarnya, sama dengan contoh C (objek) kecuali saya menggunakan x sebagai gantinya ... Saya juga tidak menginisialisasi dalam __init - ... well .. Saya lakukan, tetapi dapat dihapus karena __x didefinisikan sebagai bagian kelas ....

Outputnya adalah:

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

dan jika saya mengomentari self.x = 1234 di init maka hasilnya adalah:

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

dan jika saya menetapkan _default = Tidak Ada ke _default = 0 pada fungsi pengambil (karena semua getter harus memiliki nilai default tetapi tidak diteruskan oleh nilai properti dari apa yang saya lihat sehingga Anda dapat mendefinisikannya di sini, dan sebenarnya tidak buruk karena Anda dapat mendefinisikan default satu kali dan menggunakannya di mana-mana) yaitu: def x (self, _default = 0):

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

Catatan: Logika pengambil ada hanya untuk memiliki nilai dimanipulasi oleh itu untuk memastikan itu dimanipulasi oleh itu - sama untuk pernyataan cetak ...

Catatan: Saya sudah terbiasa dengan Lua dan mampu secara dinamis membuat 10+ pembantu ketika saya memanggil fungsi tunggal dan saya membuat sesuatu yang serupa untuk Python tanpa menggunakan properti dan berfungsi sampai tingkat tertentu, tetapi, meskipun fungsi-fungsi tersebut sedang dibuat sebelumnya sedang digunakan, masih ada masalah dengan mereka dipanggil sebelum dibuat yang aneh karena tidak dikodekan seperti itu ... Saya lebih suka fleksibilitas Lua meta-tables dan fakta saya bisa menggunakan setter / getter aktual bukannya pada dasarnya langsung mengakses variabel ... Saya suka seberapa cepat beberapa hal dapat dibangun dengan Python - misalnya program gui. walaupun yang saya rancang mungkin tidak dapat dilakukan tanpa banyak perpustakaan tambahan - jika saya kode di AutoHotkey saya dapat langsung mengakses panggilan dll yang saya butuhkan, dan hal yang sama dapat dilakukan di Java, C #, C ++,

Catatan: Keluaran kode dalam forum ini rusak - saya harus menambahkan spasi ke bagian pertama kode agar berfungsi - ketika menyalin / menempel memastikan Anda mengonversi semua spasi menjadi tab .... Saya menggunakan tab untuk Python karena di sebuah file yang 10.000 baris ukuran file bisa 512KB hingga 1MB dengan spasi dan 100 hingga 200KB dengan tab yang sama dengan perbedaan besar untuk ukuran file, dan pengurangan waktu pemrosesan ...

Tab juga dapat disesuaikan per pengguna - jadi jika Anda lebih suka lebar 2 spasi, 4, 8 atau apa pun yang dapat Anda lakukan, itu artinya bijaksana bagi pengembang dengan defisit penglihatan mata.

Catatan: Semua fungsi yang didefinisikan di kelas tidak diindentasi dengan benar karena bug dalam perangkat lunak forum - pastikan Anda membukanya jika Anda menyalin / menempel

Acecool
sumber
-3

Satu komentar: untuk saya, untuk Python 2.x, @propertytidak berfungsi seperti yang diiklankan ketika saya tidak mewarisi dari object:

class A():
    pass

tetapi bekerja ketika:

class A(object):
    pass

untuk Python 3, bekerja selalu.

Gyula Sámuel Karli
sumber
5
Itu karena di dalam Python 2, kelas yang tidak mewarisi objectadalah kelas gaya lama, dan kelas gaya lama tidak mendukung protokol deskriptor (yang merupakan propertyalat untuk bekerja seperti itu). Dalam Python 3, kelas gaya lama tidak ada lagi; semua kelas adalah apa yang kami sebut kelas gaya baru dengan Python 2.
chepner