Apa perbedaan antara "properti" Python dan "atribut"?

146

Saya umumnya bingung tentang perbedaan antara "properti" dan "atribut", dan tidak dapat menemukan sumber yang bagus untuk merinci perbedaan secara ringkas.

Carson
sumber

Jawaban:

184

Properti adalah jenis atribut khusus. Pada dasarnya, ketika Python menemukan kode berikut:

spam = SomeObject()
print(spam.eggs)

itu mendongak eggsdi spam, dan kemudian memeriksa eggsuntuk melihat apakah ia memiliki __get__, __set__atau __delete__metode - jika tidak, itu sebuah properti. Jika itu adalah properti, alih-alih hanya mengembalikan eggsobjek (seperti halnya atribut lainnya), ia akan memanggil __get__metode (karena kami melakukan pencarian) dan mengembalikan apa pun yang dikembalikan metode itu.

Informasi lebih lanjut tentang model data dan deskriptor Python .

Ethan Furman
sumber
Sepertinya properti melibatkan pemrosesan tambahan untuk mengakses nilai target ... apakah Anda tahu seberapa signifikan / lambatnya itu?
martineau
@martineau: Ya, ada satu panggilan fungsi tambahan, tetapi kemungkinan besar kerja ekstra dan waktu akan tergantung pada seberapa banyak properti itu lakukan (saran umum: lakukan / tidak / gunakan properti untuk menyembunyikan I / O).
Ethan Furman
56

Dengan properti Anda memiliki kontrol penuh pada metode pengambil, penyetel, dan deleter, yang tidak Anda miliki (jika tidak menggunakan peringatan) dengan atribut.

class A(object):
    _x = 0
    '''A._x is an attribute'''

    @property
    def x(self):
        '''
        A.x is a property
        This is the getter method
        '''
        return self._x

    @x.setter
    def x(self, value):
        """
        This is the setter method
        where I can check it's not assigned a value < 0
        """
        if value < 0:
            raise ValueError("Must be >= 0")
        self._x = value

>>> a = A()
>>> a._x = -1
>>> a.x = -1
Traceback (most recent call last):
  File "ex.py", line 15, in <module>
    a.x = -1
  File "ex.py", line 9, in x
    raise ValueError("Must be >= 0")
ValueError: Must be >= 0
neurino
sumber
Ini ("kontrol penuh") dapat dilakukan dengan atribut "non-properti" juga, hanya tanpa dekorator sederhana.
8
Saya suka bahwa jawaban ini memberikan contoh yang realistis dan berguna. Saya merasa bahwa terlalu banyak jawaban di situs ini tidak perlu menjelaskan bagaimana hal-hal bekerja di back-end tanpa menjelaskan bagaimana pengguna harus berinteraksi dengannya. Jika seseorang tidak mengerti mengapa / kapan harus menggunakan beberapa fungsi, tidak ada gunanya mengetahui cara kerjanya di belakang layar.
Tom
Jawaban ini melanggar "Zen Python - Seharusnya ada satu - dan lebih disukai hanya satu - cara yang jelas untuk melakukannya". Ada 2 cara untuk mengatur nilai x.
Tin Luu
@ TinLuu - Hanya ada satu cara untuk mengatur nilai x. Hanya ada satu cara untuk mengatur nilai _x. Fakta bahwa mereka adalah hal yang sama adalah detail implementasi. Pengguna bijak dari kelas ini tidak menggunakan _x.
menyala
1
@ TinLuu - Saya pikir kita berdua mengatakan hal yang sama dari sudut pandang yang berlawanan. Pengguna kelas seharusnya hanya melihat x. Satu arah. Jika pengguna kelas mengetahui tentang _x, mereka menggunakannya dengan risiko sendiri.
menyala
19

Secara umum, properti dan atribut adalah hal yang sama. Namun, ada dekorator properti di Python yang menyediakan akses pengambil / penyetel ke atribut (atau data lainnya).

class MyObject(object):
    # This is a normal attribute
    foo = 1

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

    @bar.setter
    def bar(self, value):
        self.foo = value


obj = MyObject()
assert obj.foo == 1
assert obj.bar == obj.foo
obj.bar = 2
assert obj.foo == 2
assert obj.bar == obj.foo
enam8
sumber
1
dapatkah Anda juga menyebutkan hasil yang diharapkan dari kode ini?
Hasan Iqbal
2
Maksud kamu apa? Bukankah itu di bagian bawah kode?
six8
12

Properti memungkinkan Anda untuk mendapatkan dan menetapkan nilai seperti yang Anda lakukan atribut normal, tetapi di bawahnya ada metode yang disebut menerjemahkannya menjadi pengambil dan penyetel untuk Anda. Ini benar-benar hanya kenyamanan untuk mengurangi pelat panggil getter dan setter.

Katakanlah misalnya, Anda memiliki kelas yang memegang beberapa koordinat x dan y untuk sesuatu yang Anda butuhkan. Untuk mengaturnya, Anda mungkin ingin melakukan sesuatu seperti:

myObj.x = 5
myObj.y = 10

Itu jauh lebih mudah untuk dilihat dan dipikirkan daripada menulis:

myObj.setX(5)
myObj.setY(10)

Masalahnya adalah, bagaimana jika suatu hari kelas Anda berubah sedemikian rupa sehingga Anda perlu mengimbangi x dan y Anda dengan beberapa nilai? Sekarang Anda harus masuk dan mengubah definisi kelas Anda dan semua kode yang menyebutnya, yang bisa sangat memakan waktu dan rawan kesalahan. Properti ini memungkinkan Anda untuk menggunakan sintaks yang sebelumnya sambil memberi Anda fleksibilitas untuk perubahan yang terakhir.

Dengan Python, Anda bisa mendefinisikan getter, setter, dan menghapus metode dengan fungsi properti. Jika Anda hanya ingin properti baca, ada juga dekorator @ properti yang dapat Anda tambahkan di atas metode Anda.

http://docs.python.org/library/functions.html#property

Falcojr
sumber
Hanya jawaban yang masuk akal praktis!
Dude156
8

Saya belajar 2 perbedaan dari situs Bernd Klein, dalam ringkasan:

1. Properti adalah cara yang lebih mudah untuk melakukan enkapsulasi data.

mis: Jika Anda memiliki atribut publik yang panjang dari Object, nanti, proyek Anda mengharuskan Anda untuk merangkumnya, yaitu: ubah ke pribadi dan berikan pengambil dan penyetel => Anda harus mengubah banyak kode yang Anda tulis sebelumnya:

#Old codes
obj1.length=obj1.length+obj2.length
#New codes(Using private attibutes and getter and setter)
obj1.set_lenght(obj1.get_length()+obj2.get_length()) #=> this is ugly

Jika Anda menggunakan @property dan @ lenght.setter => Anda tidak perlu mengubah kode lama itu

2. Properti dapat merangkum beberapa atribut

class Person:
  def __init__(self, name, physic_health, mental_health):
    self.name=name
    self.__physic_health=physic_health #physic_health is real value in range [0, 5.0]
    self.__mental_health=mental_health #mental_health is real value in range [0, 5.0]
  @property
  def condition(self):
    health=self.__physic_health+self.__mental_health
    if(health<5.0):
      return "I feel bad!"
    elif health<8.0:
      return "I am ok!"
    else:
      return "Great!"

Dalam contoh ini, __physic_healthdan __mental_healthbersifat pribadi dan tidak dapat diakses langsung dari luar, satu-satunya cara di luar kelas berinteraksi dengan mereka adalah properti terbukacondition

Timah Luu
sumber
8

Ada juga satu perbedaan tidak jelas yang saya gunakan untuk cache atau menyegarkan data, seringkali kita memiliki fungsi yang terhubung ke atribut kelas. Misalnya saya perlu membaca file sekali dan menyimpan konten yang ditugaskan ke atribut sehingga nilainya di-cache:

class Misc():
        def __init__(self):
            self.test = self.test_func()

        def test_func(self):
            print 'func running'
            return 'func value'

cl = Misc()
print cl.test
print cl.test

Keluaran:

func running
func value
func value

Kami mengakses atribut dua kali tetapi fungsi kami dipecat hanya sekali. Mengubah contoh di atas untuk menggunakan properti akan menyebabkan refresh nilai atribut setiap kali Anda mengaksesnya:

class Misc():

    @property
    def test(self):
        print 'func running'
        return 'func value'

cl = Misc()
print cl.test
print cl.test

Keluaran:

func running
func value
func running
func value
brc
sumber