Що таке getattr () саме та як я ним користуюся?


295

Я нещодавно читав про getattr()функцію . Проблема полягає в тому, що я досі не можу зрозуміти ідею його використання. Єдине, про що я розумію getattr()- getattr(li, "pop")це те саме, що дзвонити li.pop.

Я не розумів, коли книга згадує, як ви її використовуєте, щоб отримати посилання на функцію, не знаючи її назви до часу виконання. Можливо, це я, як нобій у програмуванні, взагалі. Чи може хтось пролити світло на цю тему? Коли і як саме це використовувати?


З якою частиною у вас виникають проблеми? Атрибути як рядки? Першокласні функції?
Ігнасіо Васкес-Абрамс

1
Я думаю, що моя проблема полягає в розумінні концепції getattr (). Я досі не розумію його призначення.
Теренс Понсе

@Terence не робить мою відповідь зрозумілішою?
Alois Cochard

@Alois, ваша відповідь, безумовно, очистила деякі мої сумніви, але я все ще не можу повністю зрозуміти, що таке getattr ().
Теренс Понсе

6
@ S.Lott, я так і зробив. Документація мала лише визначення, тому я був дещо плутаний щодо її використання. Я розумію гетатр тепер, коли читаю докладніше про це.
Теренс Понс

Відповіді:


87

getattr(object, 'x') повністю еквівалентна до object.x.

Є лише два випадки, коли вони getattrможуть бути корисними.

  • ви не можете писати object.x, оскільки ви не знаєте заздалегідь, який атрибут ви хочете (він походить із рядка). Дуже корисно для метапрограмування.
  • ви хочете вказати значення за замовчуванням. object.yпідніме, AttributeErrorякщо немає y. Але getattr(object, 'y', 5)повернемось 5.

2
Я вважаю, що це має бути прийнятою відповіддю. Дуже чітко і до речі.
yuqli

290

Об'єкти в Python можуть мати атрибути - атрибути даних та функції для роботи з тими (методами). Насправді кожен об’єкт має вбудовані атрибути.

Наприклад у вас є об'єкт person, який має кілька атрибутів: name, genderі т.д.

Доступ ці атрибути (будь то методи або об'єкти даних) зазвичай пишу: person.name, person.gender, person.the_method()і т.д.

Але що робити, якщо ви не знаєте ім'я атрибута під час написання програми? Наприклад, у вас є ім'я атрибута, збережене у змінній, що називається attr_name.

якщо

attr_name = 'gender'

то замість того, щоб писати

gender = person.gender

можна писати

gender = getattr(person, attr_name)

Деякі практики:

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

getattrпідніметься, AttributeErrorякщо атрибут із вказаним іменем не існує в об’єкті:

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

Але ви можете передати значення за замовчуванням як третій аргумент, який буде повернутий, якщо такого атрибута не існує:

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

Ви можете використовувати getattrразом з dirітератором для всіх імен атрибутів і отримувати їх значення:

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

Практичним шляхом для цього було б знайти усі методи, імена яких починаються з testі називати їх .

Аналогічно getattrтому, setattrщо дозволяє встановити атрибут об'єкта, що має його ім'я:

>>> setattr(person, 'name', 'Andrew')
>>> person.name  # accessing instance attribute
'Andrew'
>>> Person.name  # accessing class attribute
'Victor'
>>>

9
Тому мені здається, що його getattr(..)слід використовувати в двох сценаріях: 1. коли ім'я атрибута є значенням всередині змінної (наприклад getattr(person, some_attr)) та 2. коли нам потрібно використовувати третій позиційний аргумент для значення за замовчуванням (наприклад getattr(person, 'age', 24)). Якщо я бачу такий сценарій, як getattr(person, 'age')мені здається, він ідентичний тому, person.ageщо змушує мене думати, що person.ageце більше піфонічний. Це правильно?
wpcarro

102

Для мене, getattr найпростіше пояснити так:

Це дозволяє викликати методи на основі вмісту рядка, а не вводити ім'я методу.

Наприклад, ви не можете цього зробити:

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

тому що x не типу builtin, але str. Однак ви МОЖЕТЕ це зробити:

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

Це дозволяє динамічно з'єднуватися з об'єктами на основі введених даних. Я вважаю це корисним у роботі з користувацькими об'єктами та модулями.


2
Це досить пряма вперед та точна відповідь.
user6037143

43

Досить поширений випадок використання для getattr є зіставлення даних у функції.

Наприклад, у таких веб-рамках, як Django або Pylons, спрощене getattrвідображення URL-адреси веб-запиту на функцію, яка буде ним обробляти. Наприклад, якщо ви заглянете під капот маршрутизації Pylons, ви побачите, що (за замовчуванням, принаймні) він подрібнює URL-адресу запиту, наприклад:

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

в "замовники" та "список". Потім він шукає клас контролера з назвою CustomerController. Припускаючи, що він знаходить клас, він створює екземпляр класу, а потім використовує getattrдля отримання йогоlist методу. Потім він викликає цей метод, передаючи йому запит як аргумент.

Як тільки ви зрозумієте цю ідею, розширити функціональність веб-програми стає дуже просто: просто додайте нові методи до класів контролерів, а потім створіть посилання на своїх сторінках, які використовують відповідні URL-адреси для цих методів. Все це стало можливим завдяки getattr.


13

Ось швидкий і брудний приклад того, як клас може запускати різні версії методу збереження залежно від того, яку операційну систему він виконує 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

Тепер давайте використаємо цей клас на прикладі:

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

7

Окрім усіх дивовижних відповідей тут, є спосіб використання getattr для збереження рясних рядків коду та збереження його притисним. Ця думка виникла після жахливого подання коду, який іноді може бути необхідністю.

Сценарій

Припустимо, структура вашої директорії така:

- superheroes.py
- properties.py

І у вас є функції для отримання інформації про Thor, Iron Man, Doctor Strangeв superheroes.py. Ви дуже спритно записуєте властивості всіх у них у properties.pyкомпактний спосіб, dictа потім отримуєте доступ до них.

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'],
}

Тепер, скажімо, ви хочете повернути можливості кожного з них на вимогу superheroes.py. Отже, є такі функції, як

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

... і більше функцій, що повертають різні значення на основі клавіш і супергероя.

За допомогою getattrви можете зробити щось на кшталт:

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

Ви значно скоротили кількість рядків коду, функцій та повторення!

Ну і звичайно, якщо у вас є невірні назви, як properties_of_thorдля змінних, до них можна зробити доступ і отримати, просто зробивши це

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

ПРИМІТКА. Для цієї конкретної проблеми можуть бути розумніші способи вирішити ситуацію, але ідея полягає в тому, щоб дати зрозуміти, як використовувати getattrв потрібних місцях для написання більш чистого коду.


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

2
Не могли б ви детальніше розглянути свою відповідь, додавши трохи більше опису про рішення, яке ви надаєте?
аборисон

3

Я іноді використовую getattr(..)для лінивих ініціалізації атрибутів другорядного значення безпосередньо перед їх використанням у коді.

Порівняйте наступне:

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

До цього:

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

Перевага другого способу полягає в тому, що n_calls_to_plotвін з'являється лише навколо місця в коді, де він використовується. Це добре для читабельності, оскільки (1) ви можете відразу побачити, з якого значення воно починається, читаючи, як він використовується, (2) воно не вводить відволікання в __init__(..)метод, що в ідеалі має стосуватися концептуального стану класу , а не якийсь лічильник корисних програм, який використовується лише одним із методів функції з технічних причин, таких як оптимізація, і не має нічого спільного зі значенням об'єкта.


3

Досить часто, коли я створюю XML-файл із даних, що зберігаються в класі, я часто отримував би помилки, якби атрибут не існував або був типу None. У цьому випадку моя проблема не знала, що таке ім’я атрибута, як зазначено у вашому запитанні, а, скоріше, дані, які колись зберігалися в цьому атрибуті.

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

Якби я hasattrце робив, він би повертався, Trueнавіть якщо значення атрибуту було типу, Noneі це призведе setдо відмови моєї команди ElementTree .

hasattr(temp, 'hair')
>>True

Якщо значення атрибуту було типу None, воно getattrтакож поверне його, що призведе setдо відмови моєї команди ElementTree .

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

Зараз я використовую наступний метод для вирішення цих випадків:

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

Це коли і як я використовую getattr.


3

Ще одне використання getattr () для реалізації оператора переключення в Python. Він використовує обидва відображення, щоб отримати тип справи.

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

Мені подобається ця відповідь , але , будь ласка , виправити дрібні помилки
може

2

setattr ()

Ми використовуємо setattr, щоб додати атрибут до екземпляра нашого класу. Ми передаємо екземпляр класу, ім’я атрибута та значення.

getattr ()

За допомогою getattr ми отримуємо ці значення

Наприклад

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

employee = Employee()

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

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

print(value)

1

Я думаю, що цей приклад пояснює себе. Він запускає метод першого параметра, ім'я якого вказано у другому параметрі.

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

0

Це також уточнюється з 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)

Вік: 23 роки

Вік: 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'))

Стать: Чоловік

AttributeError: об’єкт "Person" не має атрибута "sex"


0

Я спробував у Python2.7.17

Деякі з побратимів уже відповіли. Однак я намагався викликати getattr (obj, 'set_value'), і це не виконало метод set_value, тому я змінив на getattr (obj, 'set_value') () -> Це допомагає викликати те саме.

Приклад коду:

Приклад 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.