Что такое getattr () и как его использовать?
Я читать про getattr()
функции. Проблема в том, что я до сих пор не могу понять идею его использования. Единственное, что я понимаю о getattr()
Это getattr(li, "pop")
это то же самое, что вызов li.pop
.
Я не понял, когда в книге упоминалось, как вы используете его, чтобы получить ссылку на функцию, не зная ее имени до времени выполнения. Может, это я нуб в программировании, в общем. Может ли кто-нибудь пролить свет на эту тему? Когда и как это сделать использовать именно это?
10 ответов:
вы можете просмотреть полный пример здесь:
самоанализ может использоваться для разных целей, Тот, который представлен в "Dive Into Python",-это просто способ динамического добавления функциональности (плагина) в ваше приложение.
By динамически я имею в виду без внесения изменений в основное приложение, чтобы добавить новую функцию.
с пример "погружения в Python" -простое приложение для извлечения атрибута из файла другого файла - вы можете добавить обработку нового формата файла без внесения изменений в исходное приложение.
я рекомендую вам закончить книгу. По мере того, как вы будете читать, все станет более ясным.
объекты в 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' >>>
для меня getattr проще всего объяснить таким образом:
Это позволяет вызывать методы на основе содержимого строки вместо ввода имени метода.
например, вы не можете сделать это:
obj = MyObject() for x in ['foo', 'bar']: obj.x()
потому что x не относится к типу "builtin", а "str". Однако, вы можете сделать это:
obj = MyObject() for x in ['foo', 'bar']: getattr(obj, x)()
Он позволяет динамически подключаться к объектам на основе вашего ввода. Я нашел это полезным при работе с пользовательскими объектами и модули.
довольно распространенный случай использования для
getattr
сопоставление данных с функциями.например, в веб-фреймворке, таком как Django или Pylons,
getattr
упрощает сопоставление URL-адреса веб-запроса с функцией, которая будет его обрабатывать. Если вы посмотрите под капотом маршрутизации пилонов, например, вы увидите, что (по умолчанию, по крайней мере) он разрезает URL-адрес запроса, например:http://www.example.com/customers/list
в "клиенты"и " список". Затем он ищет класс контроллера с именем
CustomerController
. Предполагаю, что он находит класс, он создает экземпляр класса, а затем использоватьgetattr
, чтобы получитьlist
метод. Затем он вызывает этот метод, передавая ему запрос в качестве аргумента.как только вы поймете эту идею, становится очень легко расширить функциональность веб-приложения: просто добавьте новые методы в классы контроллера, а затем создайте ссылки на своих страницах, которые используют соответствующие URL-адреса для этих методов. Все это стало возможным благодаря
getattr
.
вот быстрый и грязный пример того, как класс может запускать разные версии метода сохранения в зависимости от того, какая операционная система выполняется при использовании
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()
кроме всех удивительных ответов здесь, есть способ использовать
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
в нужных местах, чтобы написать более чистый код.
Я иногда использую
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__(..)
метод, который в идеале должен быть о концептуальном состоянии класса, а не какой-то счетчик полезности, который используется только одним из методов функции по техническим причинам, таким как оптимизация, и не имеет ничего общего со значением объекта.
довольно часто, когда я создаю XML-файл из данных, хранящихся в классе, я часто получаю ошибки, если атрибут не существует или имеет тип
None
. В этом случае моя проблема заключалась не в том, что я не знал, что такое имя атрибута, как указано в вашем вопросе, а скорее в том, что данные когда-либо хранились в этом атрибуте.class Pet: def __init__(self): self.hair = None self.color = None
если бы я использовал
hasattr
чтобы сделать это, он вернетсяTrue
даже если значение атрибута было типаNone
и это вызвало бы мой ElementTree сбой.hasattr(temp, 'hair') >>True
если значение атрибута было типа
None
,getattr
также вернул бы его, что вызвало бы мое 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
.
# 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')
другое использование 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]))