Apa itu getattr () persis dan bagaimana cara menggunakannya?

295

Saya baru saja membaca tentang getattr()fungsinya . Masalahnya adalah saya masih belum bisa memahami ide penggunaannya. Satu-satunya hal yang saya mengerti tentang getattr()adalah bahwa getattr(li, "pop")adalah sama dengan menelepon li.pop.

Saya tidak mengerti ketika buku itu menyebutkan bagaimana Anda menggunakannya untuk mendapatkan referensi ke suatu fungsi tanpa mengetahui namanya sampai waktu berjalan. Mungkin ini adalah saya menjadi noob dalam pemrograman, secara umum. Adakah yang bisa menjelaskan masalah ini? Kapan dan bagaimana tepatnya saya menggunakan ini?

Terence Ponce
sumber
Bagian mana yang bermasalah dengan Anda? Atribut sebagai string? Fungsi kelas satu?
Ignacio Vazquez-Abrams
1
Saya pikir masalah saya adalah memahami konsep getattr (). Saya masih tidak mengerti tujuannya.
Terence Ponce
@Terensi bukankah jawaban saya membuat semuanya menjadi lebih jelas?
Alois Cochard
@ Allois, jawaban Anda jelas menghilangkan beberapa keraguan saya, tapi saya masih belum sepenuhnya mengerti untuk apa getattr ().
Terence Ponce
6
@ S.Lott, sudah. Dokumentasi hanya memiliki definisi jadi saya agak bingung tentang penggunaannya. Saya mengerti getattr sekarang setelah membaca lebih banyak tentang itu.
Terence Ponce

Jawaban:

88

getattr(object, 'x') sepenuhnya sama dengan object.x.

Hanya ada dua kasus di mana getattrdapat bermanfaat.

  • Anda tidak dapat menulis object.x, karena sebelumnya Anda tidak tahu atribut mana yang Anda inginkan (itu berasal dari string). Sangat berguna untuk meta-programming.
  • Anda ingin memberikan nilai default. object.yakan menaikkan AttributeErrorjika tidak ada y. Tetapi getattr(object, 'y', 5)akan kembali 5.
blue_note
sumber
2
Saya merasa ini harus menjadi jawaban yang diterima. Sangat jelas dan to the point.
yuqli
290

Objek dalam Python dapat memiliki atribut - atribut data dan fungsi untuk bekerja dengan mereka (metode). Sebenarnya, setiap objek memiliki atribut bawaan.

Misalnya Anda memiliki objek person, yang memiliki beberapa atribut: name, gender, dll

Anda mengakses atribut ini (baik itu metode atau objek data) biasanya menulis: person.name, person.gender, person.the_method(), dll

Tetapi bagaimana jika Anda tidak tahu nama atribut pada saat Anda menulis program? Misalnya Anda memiliki nama atribut yang disimpan dalam variabel yang disebut attr_name.

jika

attr_name = 'gender'

lalu, alih-alih menulis

gender = person.gender

kamu bisa menulis

gender = getattr(person, attr_name)

Beberapa latihan:

Python 3.4.0 (default, Apr 11 2014, 13:05:11)

>>> class Person():
...     name = 'Victor'
...     def say(self, what):
...         print(self.name, what)
... 
>>> getattr(Person, 'name')
'Victor'
>>> attr_name = 'name'
>>> person = Person()
>>> getattr(person, attr_name)
'Victor'
>>> getattr(person, 'say')('Hello')
Victor Hello

getattrakan naik AttributeErrorjika atribut dengan nama yang diberikan tidak ada di objek:

>>> getattr(person, 'age')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute 'age'

Tapi Anda bisa memberikan nilai default sebagai argumen ketiga, yang akan dikembalikan jika atribut seperti itu tidak ada:

>>> getattr(person, 'age', 0)
0

Anda dapat menggunakannya getattrbersamaan diruntuk mengulangi semua nama atribut dan mendapatkan nilainya:

>>> dir(1000)
['__abs__', '__add__', ..., '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']

>>> obj = 1000
>>> for attr_name in dir(obj):
...     attr_value = getattr(obj, attr_name)
...     print(attr_name, attr_value, callable(attr_value))
... 
__abs__ <method-wrapper '__abs__' of int object at 0x7f4e927c2f90> True
...
bit_length <built-in method bit_length of int object at 0x7f4e927c2f90> True
...

>>> getattr(1000, 'bit_length')()
10

Penggunaan praktis untuk ini adalah untuk menemukan semua metode yang namanya dimulai dengan testdan memanggil mereka .

Mirip dengan getattrada setattryang memungkinkan Anda untuk menetapkan atribut objek yang namanya:

>>> setattr(person, 'name', 'Andrew')
>>> person.name  # accessing instance attribute
'Andrew'
>>> Person.name  # accessing class attribute
'Victor'
>>>
warvariuc
sumber
9
Jadi menurut saya yang getattr(..)harus digunakan dalam 2 skenario: 1. ketika nama atribut adalah nilai di dalam variabel (misalnya getattr(person, some_attr)) dan 2. ketika kita perlu menggunakan argumen posisi ketiga untuk nilai default (misalnya getattr(person, 'age', 24)). Jika saya melihat skenario seperti getattr(person, 'age')itu bagi saya itu identik dengan person.ageyang membuat saya berpikir bahwa person.ageitu lebih Pythonic. Apakah itu benar?
wpcarro
102

Bagi saya, getattrpaling mudah menjelaskannya dengan cara ini:

Ini memungkinkan Anda untuk memanggil metode berdasarkan pada isi string dan bukannya mengetikkan nama metode.

Misalnya, Anda tidak dapat melakukan ini:

obj = MyObject()
for x in ['foo', 'bar']:
    obj.x()

karena x bukan tipe builtin, tetapi str. Namun, Anda BISA melakukan ini:

obj = MyObject()
for x in ['foo', 'bar']:
    getattr(obj, x)()

Ini memungkinkan Anda untuk terhubung secara dinamis dengan objek berdasarkan input Anda. Saya merasa berguna ketika berhadapan dengan objek dan modul khusus.

NuclearPeon
sumber
2
Ini adalah jawaban yang cukup lurus ke depan dan tepat.
user6037143
43

Kasus penggunaan yang cukup umum getattradalah memetakan data ke fungsi.

Misalnya, dalam kerangka kerja web seperti Django atau Pylons, getattrmembuatnya mudah untuk memetakan URL permintaan web ke fungsi yang akan menanganinya. Jika Anda melihat di bawah kap perutean Pylons, misalnya, Anda akan melihat bahwa (secara default, setidaknya) itu memotong URL permintaan, seperti:

http://www.example.com/customers/list

ke dalam "pelanggan" dan "daftar". Kemudian mencari kelas controller bernama CustomerController. Dengan asumsi ia menemukan kelas, ia membuat turunan kelas dan kemudian menggunakan getattruntuk mendapatkan listmetodenya. Kemudian memanggil metode itu, meneruskannya permintaan sebagai argumen.

Setelah Anda memahami ide ini, menjadi sangat mudah untuk memperluas fungsionalitas aplikasi web: cukup tambahkan metode baru ke kelas controller, dan kemudian buat tautan di halaman Anda yang menggunakan URL yang sesuai untuk metode tersebut. Semua ini dimungkinkan oleh getattr.

Robert Rossney
sumber
13

Berikut adalah contoh cepat dan kotor tentang bagaimana suatu kelas dapat mem-boot versi berbeda dari metode penyimpanan tergantung pada sistem operasi mana yang sedang dieksekusi saat menggunakan getattr().

import os

class Log(object):
    def __init__(self):
        self.os = os.name
    def __getattr__(self, name):
        """ look for a 'save' attribute, or just 
          return whatever attribute was specified """
        if name == 'save':
            try:
                # try to dynamically return a save 
                # method appropriate for the user's system
                return getattr(self, self.os)
            except:
                # bail and try to return 
                # a default save method
                return getattr(self, '_save')
        else:
            return getattr(self, name)

    # each of these methods could have save logic specific to 
    # the system on which the script is executed
    def posix(self): print 'saving on a posix machine'
    def nt(self): print 'saving on an nt machine'
    def os2(self): print 'saving on an os2 machine'
    def ce(self): print 'saving on a ce machine'
    def java(self): print 'saving on a java machine'
    def riscos(self): print 'saving on a riscos machine'
    def _save(self): print 'saving on an unknown operating system'

    def which_os(self): print os.name

Sekarang mari kita gunakan kelas ini dalam contoh:

logger = Log()

# Now you can do one of two things:
save_func = logger.save
# and execute it, or pass it along 
# somewhere else as 1st class:
save_func()

# or you can just call it directly:
logger.save()

# other attributes will hit the else 
# statement and still work as expected
logger.which_os()
Josh
sumber
7

Selain semua jawaban luar biasa di sini, ada cara yang digunakan getattruntuk menyimpan baris kode yang berlebihan dan menjaganya tetap nyaman. Pikiran ini muncul setelah representasi kode yang mengerikan yang terkadang menjadi keharusan.

Skenario

Misalkan struktur direktori Anda adalah sebagai berikut:

- superheroes.py
- properties.py

Dan, Anda memiliki fungsi untuk mendapatkan informasi tentang Thor, Iron Man, Doctor Strangedi superheroes.py. Anda dengan cerdas menuliskan properti mereka semua properties.pydalam sebuah compact dictdan kemudian mengaksesnya.

properties.py

thor = {
    'about': 'Asgardian god of thunder',
    'weapon': 'Mjolnir',
    'powers': ['invulnerability', 'keen senses', 'vortex breath'], # and many more
}
iron_man = {
    'about': 'A wealthy American business magnate, playboy, and ingenious scientist',
    'weapon': 'Armor',
    'powers': ['intellect', 'armor suit', 'interface with wireless connections', 'money'],
}
doctor_strange = {
    'about': ' primary protector of Earth against magical and mystical threats',
    'weapon': 'Magic',
    'powers': ['magic', 'intellect', 'martial arts'],
}

Sekarang, katakanlah Anda ingin mengembalikan kemampuan masing-masing sesuai permintaan superheroes.py. Jadi, ada fungsi seperti

from .properties import thor, iron_man, doctor_strange


def get_thor_weapon():
    return thor['weapon']


def get_iron_man_bio():
    return iron_man['about']


def get_thor_powers():
    return thor['powers']

... dan lebih banyak fungsi yang mengembalikan nilai berbeda berdasarkan tombol dan pahlawan super.

Dengan bantuan getattr, Anda dapat melakukan sesuatu seperti:

from . import properties


def get_superhero_weapon(hero):
    superhero = getattr(properties, hero)
    return superhero['weapon']


def get_superhero_powers(hero):
    superhero = getattr(properties, hero)
    return superhero['powers']

Anda sangat mengurangi jumlah baris kode, fungsi, dan pengulangan!

Oh dan tentu saja, jika Anda memiliki nama buruk seperti properties_of_thoruntuk variabel, mereka dapat dibuat dan diakses hanya dengan melakukan

def get_superhero_weapon(hero):
    superhero = 'properties_of_{}'.format(hero)
    all_properties = getattr(properties, superhero)
    return all_properties['weapon']

CATATAN: Untuk masalah khusus ini, mungkin ada cara yang lebih cerdas untuk menghadapi situasi ini, tetapi idenya adalah untuk memberikan wawasan tentang penggunaan getattrdi tempat yang tepat untuk menulis kode yang lebih bersih.

unixia
sumber
3
# getattr

class hithere():

    def french(self):
        print 'bonjour'

    def english(self):
        print 'hello'

    def german(self):
        print 'hallo'

    def czech(self):
        print 'ahoj'

    def noidea(self):
        print 'unknown language'


def dispatch(language):
    try:
        getattr(hithere(),language)()
    except:
        getattr(hithere(),'noidea')()
        # note, do better error handling than this

dispatch('french')
dispatch('english')
dispatch('german')
dispatch('czech')
dispatch('spanish')
Kduyehj
sumber
2
Bisakah Anda menguraikan lebih banyak jawaban Anda dengan menambahkan sedikit deskripsi tentang solusi yang Anda berikan?
abarisone
3

Kadang-kadang saya gunakan getattr(..)untuk malas menginisialisasi atribut yang tidak penting sebelum mereka digunakan dalam kode.

Bandingkan yang berikut ini:

class Graph(object):
    def __init__(self):
        self.n_calls_to_plot = 0

    #...
    #A lot of code here
    #...

    def plot(self):
        self.n_calls_to_plot += 1

Untuk ini:

class Graph(object):
    def plot(self):
        self.n_calls_to_plot = 1 + getattr(self, "n_calls_to_plot", 0)

Keuntungan dari cara kedua adalah bahwa n_calls_to_plothanya muncul di sekitar tempat dalam kode di mana ia digunakan. Ini bagus untuk keterbacaan, karena (1) Anda dapat segera melihat nilai apa yang dimulai ketika membaca bagaimana itu digunakan, (2) itu tidak memperkenalkan gangguan ke dalam __init__(..)metode, yang idealnya harus tentang keadaan konseptual kelas. , daripada beberapa penghitung utilitas yang hanya digunakan oleh salah satu metode fungsi karena alasan teknis, seperti optimasi, dan tidak ada hubungannya dengan makna objek.

Evgeni Sergeev
sumber
3

Cukup sering ketika saya membuat file XML dari data yang disimpan di kelas saya sering menerima kesalahan jika atribut tidak ada atau bertipe None. Dalam hal ini, masalah saya tidak tidak tahu apa nama atribut itu, seperti yang dinyatakan dalam pertanyaan Anda, melainkan apakah data pernah disimpan dalam atribut itu.

class Pet:
    def __init__(self):
        self.hair = None
        self.color = None

Jika saya terbiasa hasattrmelakukan ini, itu akan kembali Truebahkan jika nilai atribut adalah tipe Nonedan ini akan menyebabkan setperintah ElementTree saya gagal.

hasattr(temp, 'hair')
>>True

Jika nilai atribut adalah tipe None, getattrjuga akan mengembalikannya yang akan menyebabkan setperintah ElementTree saya gagal.

c = getattr(temp, 'hair')
type(c)
>> NoneType

Saya menggunakan metode berikut untuk menangani kasus-kasus ini sekarang:

def getRealAttr(class_obj, class_attr, default = ''):
    temp = getattr(class_obj, class_attr, default)
    if temp is None:
        temp = default
    elif type(temp) != str:
        temp = str(temp)
    return temp

Inilah saatnya dan bagaimana saya menggunakan getattr.

btathalon
sumber
3

Penggunaan lain dari getattr () dalam mengimplementasikan pernyataan switch dengan Python. Ini menggunakan kedua refleksi untuk mendapatkan jenis case.

import sys

class SwitchStatement(object):
    """ a class to implement switch statement and a way to show how to use gettattr in Pythion"""

    def case_1(self):
        return "value for case_1"

    def case_2(self):
        return "value for case_2"

    def case_3(self):
        return "value for case_3"

    def case_4(self):
        return "value for case_4"

    def case_value(self, case_type=1):
        """This is the main dispatchmethod, that uses gettattr"""
        case_method = 'case_' + str(case_type)
        # fetch the relevant method name
        # Get the method from 'self'. Default to a lambda.
        method = getattr(self, case_method, lambda: "Invalid case type")
        # Call the method as we return it
        return method()

def main(_):
    switch = SwitchStatement()
    print swtich.case_value(_)

if __name__ == '__main__':
    main(int(sys.argv[1]))
Jules Damji
sumber
Aku seperti jawaban ini tapi tolong memperbaiki kesalahan ketik kecil
mungkin
2

setattr ()

Kami menggunakan setattr untuk menambahkan atribut ke instance kelas kami. Kami melewati instance kelas, nama atribut, dan nilai.

getattr ()

Dengan getattr kami mengembalikan nilai-nilai ini

Sebagai contoh

Employee = type("Employee", (object,), dict())

employee = Employee()

# Set salary to 1000
setattr(employee,"salary", 1000 )

# Get the Salary
value = getattr(employee, "salary")

print(value)
kuldeep Mishra
sumber
1

Saya pikir contoh ini cukup jelas. Itu menjalankan metode parameter pertama, yang namanya diberikan dalam parameter kedua.

class MyClass:
   def __init__(self):
      pass
   def MyMethod(self):
      print("Method ran")

# Create an object
object = MyClass()
# Get all the methods of a class
method_list = [func for func in dir(MyClass) if callable(getattr(MyClass, func))]
# You can use any of the methods in method_list
# "MyMethod" is the one we want to use right now

# This is the same as running "object.MyMethod()"
getattr(object,'MyMethod')()
Dersu Giritlioğlu
sumber
0

Ini juga mengklarifikasi dari https://www.programiz.com/python-programming/methods/built-in/getattr

class Person:
    age = 23
    name = "Adam"

person = Person()
print('The age is:', getattr(person, "age"))
print('The age is:', person.age)

Usia adalah: 23

Usia adalah: 23

class Person:
    age = 23
    name = "Adam"

person = Person()

# when default value is provided
print('The sex is:', getattr(person, 'sex', 'Male'))

# when no default value is provided
print('The sex is:', getattr(person, 'sex'))

Jenis kelaminnya adalah: Pria

AttributeError: Objek 'Orang' tidak memiliki atribut 'seks'

Barny
sumber
0

Saya sudah mencoba di Python2.7.17

Beberapa rekan sudah menjawab. Namun saya telah mencoba memanggil getattr (obj, 'set_value') dan ini tidak mengeksekusi metode set_value, Jadi saya berubah menjadi getattr (obj, 'set_value') () -> Ini membantu untuk memanggil yang sama.

Kode Contoh:

Contoh 1:

    class GETATT_VERIFY():
       name = "siva"
       def __init__(self):
           print "Ok"
       def set_value(self):
           self.value = "myself"
           print "oooh"
    obj = GETATT_VERIFY()
    print getattr(GETATT_VERIFY, 'name')
    getattr(obj, 'set_value')()
    print obj.value
keseimbangan air liur
sumber