В Java все переменные, которые вы объявляете, на самом деле являются «ссылками» на объекты (или примитивы), а не самими объектами.
При попытке выполнить один метод объекта , ссылка просит живой объект выполнить этот метод. Но если ссылка ссылается на NULL (ничего, нуль, void, nada), то нет способа, которым метод будет выполнен. Тогда runtime сообщит вам об этом, выбросив исключение NullPointerException.
Ваша ссылка «указывает» на нуль, таким образом, «Null -> Pointer».
Объект живет в памяти виртуальной машины пространство и единственный способ доступа к нему - использовать ссылки this
. Возьмем этот пример:
public class Some {
private int id;
public int getId(){
return this.id;
}
public setId( int newId ) {
this.id = newId;
}
}
И в другом месте вашего кода:
Some reference = new Some(); // Point to a new object of type Some()
Some otherReference = null; // Initiallly this points to NULL
reference.setId( 1 ); // Execute setId method, now private var id is 1
System.out.println( reference.getId() ); // Prints 1 to the console
otherReference = reference // Now they both point to the only object.
reference = null; // "reference" now point to null.
// But "otherReference" still point to the "real" object so this print 1 too...
System.out.println( otherReference.getId() );
// Guess what will happen
System.out.println( reference.getId() ); // :S Throws NullPointerException because "reference" is pointing to NULL remember...
Это важно знать - когда больше нет ссылок на объект (в пример выше, когда reference
и otherReference
оба указывают на null), тогда объект «недоступен». Мы не можем работать с ним, поэтому этот объект готов к сбору мусора, и в какой-то момент VM освободит память, используемую этим объектом, и выделит другую.
Здесь вы можете посмотреть полный пример:
Introspection может использоваться для разных целей, один из которых представлен в «Dive Into Python» - это просто способ динамически добавлять функциональность (плагин) в ваше приложение.
Динамически я имею в виду без сделать модификацию в основном приложении, чтобы добавить новую функцию.
Взяв пример «Dive Into Python» - простое приложение для извлечения атрибута из файла другого файла - вы можете добавить обработка нового формата файла без внесения изменений в исходное приложение.
Я рекомендую вам закончить книгу. Когда вы читаете, все станет более ясным.
Довольно часто, когда я создаю файл XML из данных, хранящихся в классе, я часто получал ошибки, если атрибут не существовал или имел тип None
. В этом случае моя проблема не зная, что такое имя атрибута, как указано в вашем вопросе, а скорее данные, хранящиеся в этом атрибуте.
class Pet:
def __init__(self):
self.hair = None
self.color = None
Если я использовал hasattr
для сделайте это, он вернет True
, даже если значение атрибута имеет тип None
, и это приведет к ошибке моей команды ElementTree set
.
hasattr(temp, 'hair')
>>True
Если значение атрибута имело тип None
, getattr
также вернет его, что приведет к сбою моей команды ElementTree set
.
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 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__(..)
, который в идеале должен быть о концептуальном состоянии класса, а не некоторый счетчик коммунальных услуг, который используется только одним из методов функции по техническим причинам, таким как оптимизация, и не имеет ничего общего со значением объекта.
Другое использование getattr () при реализации инструкции switch в Python. Он использует оба отражения для получения типа 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]))
Вот быстрый и грязный пример того, как класс может запускать разные версии метода сохранения, в зависимости от того, какую операционную систему он выполняет при использовании 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
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 проще всего объяснить следующим образом:
Позволяет вам вызывать методы на основе содержимого строки вместо ввода имени метода.
Например , вы не можете этого сделать:
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 веб-запроса с функцией, которая будет обрабатывать ее. Если вы посмотрите, например, на капот маршрутизации Pylons, вы увидите, что (по крайней мере, по умолчанию) он разбивает URL-адрес запроса, например:
http://www.example.com/customers/list
на «клиентов» и «клиентов», список". Затем он ищет класс контроллера с именем CustomerController
. Предполагая, что он находит класс, он создает экземпляр класса, а затем использует getattr
для получения своего метода list
. Затем он вызывает этот метод, передавая ему запрос в качестве аргумента.
Как только вы поймете эту идею, становится очень просто расширить функциональность веб-приложения: просто добавьте новые методы в классы контроллера и затем создайте ссылки на своих страницах, которые используют соответствующие URL-адреса для этих методов. Все это стало возможным благодаря getattr
.
Помимо всех удивительных ответов здесь есть способ использовать 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
в правильных местах, чтобы писать более чистый код.
Объекты в Python могут иметь атрибуты (фактически, каждый объект имеет встроенные атрибуты - атрибуты данных и методы (функции - это значения, т. е. объекты), чтобы работать с ними).
Например у вас есть объект person
, который имеет несколько атрибутов: name
, gender
и т. д.
Доступ к этим атрибутам (будь то методы или объекты данных) обычно записывается: person.name
, person.gender
, person.the_method()
и т. д.
Но что, если вы не знаете имя атрибута в момент написания программы? Например, у вас есть имя атрибута, хранящееся в переменной под названием attr_name
.
, если
attr_name = 'gender'
, вместо записи
gender = person.gender
вы можете write
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
, если атрибут с заданным именем не существует в object:
>>> 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(..)
следует использовать в двух сценариях: 1. когда имя атрибута является значением внутри переменной (например, getattr(person, some_attr)
) и 2. когда нам нужно использовать третий позиционный аргумент для значения по умолчанию значение (например, getattr(person, 'age', 24)
). Если я вижу сценарий, подобный getattr(person, 'age')
, мне кажется, что он идентичен person.age
, что заставляет меня думать, что person.age
более Pythonic. Это верно?
– wpcarro
24 October 2016 в 22:01