Что такое getattr () и как мне его использовать?


295

Я недавно прочитал о getattr()функции . Проблема в том, что я до сих пор не могу понять идею его использования. Единственное, что я понимаю getattr(), getattr(li, "pop")это то же самое, что звонить li.pop.

Я не понял, когда в книге упоминалось, как вы используете ее, чтобы получить ссылку на функцию, не зная ее имени до времени выполнения. Может быть, это я нуб в программировании, в целом. Может ли кто-нибудь пролить свет на эту тему? Когда и как мне это использовать?


С какой частью у вас проблемы? Атрибуты в виде строк? Первоклассные функции?
Игнасио Васкес-Абрамс

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

@ Теренс, разве мой ответ не проясняет ситуацию?
Alois Cochard

@Alois, твой ответ определенно снял некоторые мои сомнения, но я до сих пор не могу полностью понять, для чего предназначен getattr ().
Теренс Понсе

6
@ S.Lott, я сделал. В документации было только определение, поэтому я был немного озадачен его использованием. Теперь я понимаю getattr, прочитав об этом больше.
Теренс Понсе

Ответы:


88

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(..)следует использовать в 2 сценариях: 1. когда имя атрибута является значением внутри переменной (например getattr(person, some_attr)) и 2. когда нам нужно использовать третий позиционный аргумент для значения по умолчанию (например, getattr(person, 'age', 24)). Если я вижу такой сценарий, как getattr(person, 'age')мне кажется, он идентичен, и person.ageэто заставляет меня думать, что person.ageэто более Pythonic. Это правильно?
wpcarro

102

Для меня getattrпроще всего объяснить это так:

Это позволяет вам вызывать методы на основе содержимого строки вместо того, чтобы вводить имя метода.

Например, вы не можете сделать это:

obj = MyObject()
for x in ['foo', 'bar']:
    obj.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
Не могли бы вы более подробно проработать свой ответ, добавив немного больше описания предлагаемого вами решения?
abarisone

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 () для реализации оператора switch в 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.