Что такое introspection python
Перейти к содержимому

Что такое introspection python

  • автор:

Интроспекция в Python

Многие языки программирования поддерживают интроспекцию, и Python не является исключением. В общем, в контексте объектно-ориентированных языков программирования, интроспекция — это способность объекта во время выполнения получить тип, доступные атрибуты и методы, а также другую информацию, необходимую для выполнения дополнительных операций с объектом.

В этой статье мы рассмотрим пять самых полезных функций интроспекции в Python.

dir()

Первая функция — это функция dir() . Она предоставляет список атрибутов и методов, доступных для указанного объекта, который может быть объявленной переменной или функцией.

>>> a = [1, 2, 3] >>> dir(a) ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

Как вы можете заметить, значение, которое вернула функция dir() — это отсортированный в алфавитном порядке список. Это подразумевает, что мы можем проверить существование определенного атрибута или метода, чтобы увидеть, может ли объект выполнить эту операцию. Пример приведен ниже.

>>> b = [1, 2, 3] >>> b_dir = dir(b) >>> 'index' in b_dir True >>> 'pop' in b_dir True

При вызове функции dir() без аргумента она возвращает имена, доступные в локальной области видимости, как показано ниже. Это может быть полезно, если вы хотите проверить, что было определено и использовано в вашей программе.

>>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'b', 'b_dir']

type()

Другой часто используемой функцией интроспекции является функция type() . Как видно из названия, эта функция возвращает тип объекта, который может быть примитивным типом данных, объектом, классом или модулем. Давайте посмотрим на примеры ниже:

>>> type(1.2) >>> type([1, 2, 3]) >>> type((3, ‘Three’)) >>> def do_something(): . pass . >>> type(do_something) >>> class Fruit: . pass . >>> type(Fruit) >>> type(Fruit()) >>> import os >>> type(os)

Что мы можем сделать с возвращаемыми значениями из функции type() ? Мы можем напрямую сравнить возвращаемое значение с типом, который мы хотим проверить, используя == или is . Ниже приведено несколько примеров.

>>> type(1.2) == int False >>> type(1.2) == float True >>> type([1,2]) == list True >>> type((1,2)) is tuple True

isinstance()

Еще одной из функций интроспекции, которая особенно полезна, является функция isinstance() . Используя эту функцию, мы можем определить, является ли определенный объект экземпляром указанного класса. Простой пример приведен ниже.

>>> isinstance([1,2], list) True >>> isinstance([1,2], tuple) False >>> isinstance((1,2), tuple) True

Следует также отметить, что функция isinstance() может принимать кортеж в качестве второго аргумента, как показано ниже.

>>> isinstance(1, (int, float, tuple)) True >>> isinstance(1, int) or isinstance(1, float) or isinstance(1, tuple)

Рассмотрим более практический пример использования, который включает в себя пользовательский класс:

>>> class Fruit: . pass . >>> apple = Fruit() >>> isinstance(apple, Fruit) True

Возможно, вы заметили, что и type() , и isinstance() могут быть использованы для определения того, принадлежит ли указанный объект определенному типу. Тем не менее, они не одинаковы.

Когда мы используем type() , чтобы определить, принадлежит ли объект определенному типу, мы проводим сравнение один к одному. По сути, мы сравниваем тип объекта с типом, который мы указали, чтобы проверить, совпадают ли они.

А вот isinstance() является более гибкой функцией. Фактически она определяет, является ли объект экземпляром указанного класса (классов) или его подкласса. Для isinstance() экземпляр подкласса также является экземпляром базового класса. Другими словами, она сравнивает объект со списком потенциально релевантных классов, что является своего рода сравнением один к нескольким. На рисунке ниже приведен пример работы функции.

интроспекция в python

hasattr()

Иногда, прежде чем получить доступ к атрибуту объекта, мы можем захотеть проверить, есть ли у него этот атрибут. Мы не хотим видеть следующую ошибку:

>>> class Fruit: . pass . >>> Fruit().tasty Traceback (most recent call last): File "", line 1, in AttributeError: 'Fruit' object has no attribute 'tasty'

Чтобы подобного не произошло, мы можем с помощью функции hasattr() проверить, есть ли у объекта такой атрибут, прежде чем обращаться к нему.

>>> class Fruit: . tasty = True . >>> fruit = Fruit() >>> if hasattr(fruit, 'tasty'): . print('The fruit is tasty') . else: . print('The fruit is not tasty') . The fruit is tasty

Возможно, вы не обратили на это внимание, но мы можем использовать функцию dir() для достижения того же самого результата.

Как вы помните, функция dir() возвращает доступные атрибуты и методы для указанного объекта, так что мы можем напрямую сравнить, является ли атрибут одним из элементов возвращаемого списка. Ниже приведен обновленный код.

>>> class Fruit: . tasty = True . >>> fruit = Fruit() >>> if 'tasty' in dir(fruit): . print('The fruit is tasty') . else: . print('The fruit is not tasty') . The fruit is tasty

id()

И последняя, но не менее важная функция интроспекции в Python — это функция id() . Она возвращает идентификатор указанного объекта. В CPython идентификатор объекта — это адрес объекта в памяти, который является уникальным для всех существующих объектов. Объекты могут иметь одинаковый идентификатор, если периоды их существования не пересекаются. Ниже приведен простой пример этой функции.

a = 2 b = 1 id(a) 140339209865072 id(b) 140339209865096

Один из распространенных примеров кода в Python — это обмен значений двух переменных. Этого можно добиться, просто выполнив следующий код a, b = b, a . Давайте посмотрим, что происходит после обмена.

id(a) 140339209865096 id(b) 140339209865072 a, b (1, 2)

Как мы можем заметить, эти две переменные успешно поменялись местами, что видно из их адресов в памяти и значений.

Выводы

Проводить интроспекцию в Python очень удобно.

Если вы хотите узнать о более продвинутых методах интроспекции в Python, то можете изучить модуль inspect.

Пять функций, которые были рассмотрены в этой статье, являются наиболее распространенными. Вы смело можете использовать их в своих проектах на Python.

15. Object introspection¶

In computer programming, introspection is the ability to determine the type of an object at runtime. It is one of Python’s strengths. Everything in Python is an object and we can examine those objects. Python ships with a few built-in functions and modules to help us.

15.1. dir ¶

In this section we will learn about dir and how it facilitates us in introspection.

It is one of the most important functions for introspection. It returns a list of attributes and methods belonging to an object. Here is an example:

my_list = [1, 2, 3] dir(my_list) # Output: ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', # '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', # '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', # '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', # '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', # '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', # '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', # 'remove', 'reverse', 'sort'] 

Our introspection gave us the names of all the methods of a list. This can be handy when you are not able to recall a method name. If we run dir() without any argument then it returns all names in the current scope.

15.2. type and id ¶

The type function returns the type of an object. For example:

print(type('')) # Output: print(type([])) # Output: print(type(<>)) # Output: print(type(dict)) # Output: print(type(3)) # Output:

id returns the unique ids of various objects. For instance:

name = "Yasoob" print(id(name)) # Output: 139972439030304 

15.3. inspect module¶

The inspect module also provides several useful functions to get information about live objects. For example you can check the members of an object by running:

import inspect print(inspect.getmembers(str)) # Output: [('__add__',  

There are a couple of other methods as well which help in introspection. You can explore them if you wish.

© Copyright 2017, Muhammad Yasoob Ullah Khalid Revision 9b6262ee .

Read the Docs v: latest

Versions latest Downloads pdf html epub On Read the Docs Project Home Builds Free document hosting provided by Read the Docs.

Saved searches

Use saved searches to filter your results more quickly

Cancel Create saved search

You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.

This repository has been archived by the owner on Aug 6, 2022. It is now read-only.

Deserialize ROS messages that are unknown at compilation time

License

facontidavide/ros_type_introspection

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Switch branches/tags
Branches Tags
Could not load branches
Nothing to show
Could not load tags
Nothing to show

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Cancel Create

  • Local
  • Codespaces

HTTPS GitHub CLI
Use Git or checkout with SVN using the web URL.
Work fast with our official CLI. Learn more about the CLI.

Sign In Required

Please sign in to use Codespaces.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching Xcode

If nothing happens, download Xcode and try again.

Launching Visual Studio Code

Your codespace will open once ready.

There was a problem preparing your codespace, please try again.

Latest commit

Git stats

Files

Failed to load latest commit information.

Latest commit message
Commit time
September 3, 2020 12:51
November 6, 2019 19:17
October 13, 2016 15:07
June 22, 2016 05:33
September 3, 2020 12:51
September 30, 2019 23:56
June 22, 2016 14:01
August 25, 2020 08:30
September 20, 2017 15:02
September 3, 2020 12:51

README.md

This library will be discontinued .

A large refactoring has been done to create a better and simpler library. All the development effort will be moved there.

Have a look at ros_msg_parser

Ros Message Introspection

This simple library extracts information from a ROS Message, even when its type is unknown at compilation time.

Have you ever wanted to build an app that can subscribe to any topic and extract its content, or can read data from any rosbag ? What if the topic and/or the bag contains user defined ROS types ignored at compilation time?

The common solution in the ROS ecosystem is to use Python, that provides the needed introspection. Tools, for instance, like rqt_plot and rqt_bag took this approach. This library implements a C++ alternative.

This library is particularly useful to extract data from two type-erasing classes provided by ROS itself:

  1. topic_tools::ShapeShifter: a type used to subscribe to any topic, regardless of the original type.
  2. rosbag::MessageInstance: the generic type commonly used to read data from a ROS bag.

Please take a look to the examples and unit tests to see how to use the library.

Some background

The ROS Message Types can be described as a Interface Description Language. This approach is very well known and commonly used on the web and in distributed systems in general.

A rosmsg is defined by the user; an «IDL compiler», i.e. gencpp, reads this schema and generates a header file that contains the source code that the user shall include in his/her applications. The inclusion of this header file is needed on both the publisher and the subscriber sides.

This approach provides strong and type-safe contracts between the producer and the consumer of the message and, additionally, is needed to implements a fast serialization / deserialization mechanism.

The only «problem» is that in very few use cases (for instance if you want to build a plugin to load ROS bags in MATLAB) you don’t know in advance which ROS Messages you will need to read. Therefore, you won’t be able to include the necessary header files.

Acknowledgements

This library is inspired by these other libraries matlab_rosbag and cpp_introspection.

Основные принципы программирования: интроспекция и рефлексия

На данный момент этот блок не поддерживается, но мы не забыли о нём! Наша команда уже занята его разработкой, он будет доступен в ближайшее время.

Часто во время работы программы нам бывает нужна информация о данных — например, какой у них тип или являются ли они экземпляром класса (в ООП). Опираясь на эти знания, нам нужно проводить над данными некоторые операции, или даже изменять их — но необходимого вида данных у нас может и не быть! Если вы ничего не поняли, не расстраивайтесь — мы подробно во всём разберёмся. Всё, что я здесь описал — это иллюстрация целей двух возможностей, присутствующих почти в каждом современном языке программирования: интроспекции и рефлексии.

Интроспекция

Интроспекция — это способность программы исследовать тип или свойства объекта во время работы программы. Как мы уже упоминали, вы можете поинтересоваться, каков тип объекта, является ли он экземпляром класса. Некоторые языки даже позволяют узнать иерархию наследования объекта. Возможность интроспекции есть в таких языках, как Ruby, Java, PHP, Python, C++ и других. В целом, инстроспекция — это очень простое и очень мощное явление. Вот несколько примеров использования инстроспекции:

// Java if(obj instanceof Person)
//PHP if ($obj instanceof Person) < // делаем что угодно > 

В Python самой распространённой формой интроспекции является использование метода dir для вывода списка атрибутов объекта:

# Python class foo(object): def __init__(self, val): self.x = val def bar(self): return self.x . dir(foo(5)) => ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', 'bar', 'x'] 

В Ruby интроспекция очень полезна — в частности из-за того, как устроен сам язык. В нём всё является объектами — даже класс — и это приводит к интересным возможностям в плане наследования и рефлексии (об этом ниже). Если вы хотите узнать об этом больше, советую прочитать мини-цикл Metaprogramming in Ruby.

Прим. перев. Также не будет лишним прочитать нашу статью, посвящённую интроспекции в Ruby.

Вот несколько простых примеров интроспекции с использованием IRB (Interactive Ruby Shell):

# Ruby $ irb irb(main):001:0> A=Class.new => A irb(main):002:0> B=Class.new A => B irb(main):003:0> a=A.new => # irb(main):004:0> b=B.new => # irb(main):005:0> a.instance_of? A => true irb(main):006:0> b.instance_of? A => false irb(main):007:0> b.kind_of? A => true 

Вы также можете узнать у объекта, экземпляром какого класса он является, и даже “сравнить” классы.

# Ruby irb(main):008:0> A.instance_of? Class => true irb(main):009:0> a.class => A irb(main):010:0> a.class.class => Class irb(main):011:0> A > B => true irb(main):012:0> B true 

Однако интроспекция — это не рефлексия; рефлексия позволяет нам использовать ключевые принципы интроспекции и делать действительно мощные вещи с нашим кодом.

Рефлексия

Интроспекция позволяет вам изучать атрибуты объекта во время выполнения программы, а рефлексия — манипулировать ими. Рефлексия — это способность компьютерной программы изучать и модифицировать свою структуру и поведение (значения, мета-данные, свойства и функции) во время выполнения. Простым языком: она позволяет вам вызывать методы объектов, создавать новые объекты, модифицировать их, даже не зная имён интерфейсов, полей, методов во время компиляции. Из-за такой природы рефлексии её труднее реализовать в статически типизированных языках, поскольку ошибки типизации возникают во время компиляции, а не исполнения программы (подробнее об этом здесь). Тем не менее, она возможна, ведь такие языки, как Java, C# и другие допускают использование как интроспекции, так и рефлексии (но не C++, он позволяет использовать лишь интроспекцию).

По той же причине рефлексию проще реализовать в интерпретируемых языках, поскольку когда функции, объекты и другие структуры данных создаются и вызываются во время работы программы, используется какая-то система распределения памяти. Интерпретируемые языки обычно предоставляют такую систему по умолчанию, а для компилируемых понадобится дополнительный компилятор и интерпретатор, который следит за корректностью рефлексии.

Мне кажется, что мы сказали много об определении рефлексии, но смысла это пока несёт мало. Давайте взглянем на примеры кода ниже (с рефлексией и без), каждый из которых создаёт объект класса Foo и вызывает метод hello.

// ECMAScript - как JavaScript // Без рефлексии new Foo().hello() // С рефлексией // предполагаем, что Foo принадлежит this new this['Foo']()['hello']() // или не предполагаем new (eval('Foo'))()['hello']() // или вообще не заморачиваемся eval('new Foo().hello()') 
// Java // Без рефлексии Foo foo = new Foo(); foo.hello(); // С рефлексией Object foo = Class.forName("complete.classpath.and.Foo").newInstance(); // Альтернатива: Object foo = Foo.class.newInstance(); Method m = foo.getClass().getDeclaredMethod("hello", new Class[0]); m.invoke(foo); 
# Python # Без рефлексии obj = Foo() obj.hello() # С рефлексией class_name = "Foo" method = "hello" obj = globals()[class_name]() getattr(obj, method)() # С eval eval("Foo().hello()") 
# Ruby # Без рефлексии obj = Foo.new obj.hello # С рефлексией class_name = "Foo" method = :hello obj = Kernel.const_get(class_name).new obj.send method # С eval eval "Foo.new.hello" 

Этот список отнюдь не исчерпывает возможности рефлексии. Это очень мощный принцип, который к тому же является обычной практикой в метапрограммировании. Тем не менее, при использовании рефлексии нужно быть очень внимательным. Хотя у неё и есть свои преимущества, код, использующий рефлексию, значительно менее читаем, он затрудняет отладку, а также открывает двери по-настоящему плохим вещами, например, инъекции кода через выражения eval.

Eval-выражения

Некоторые рефлективные языки предоставляют возможность использования eval-выражений — выражений, которые распознают значение (обычно строку) как выражение. Такие утверждения — это самый мощный принцип рефлексии и даже метапрограммирования, но также и самый опасный, поскольку они представляют собой угрозу безопасности.

Рассмотрим следующий пример кода на Python, который принимает данные из стороннего источника в Сети (это одна из причин, по которой люди пользуются eval-выражениями):

session['authenticated'] = False data = get_data() foo = eval(data) 

Защита программы будет нарушена, если кто-то передаст в метод get_data() такую строку:

"session.update(authenticated=True)" 

Для безопасного использования eval-утверждений нужно сильно ограничивать формат входных данных — и обычно это лишь занимает лишнее время.

Заключение

Интроспекция и рефлексия — это очень мощные инструменты современных языков, и их понимание может позволить вам писать по-настоящему крутой код. Ещё раз отметим: интроспекция — это изучение атрибутов объекта, а рефлексия — это манипуляция ими. Будьте внимательны при использовании рефлексии, поскольку она может сделать ваш код нечитаемым и уязвимым. Чем больше сила, тем больше и ответственность — вот девиз всего, что связано с метапрограммированием.

Следите за новыми постами по любимым темам

Подпишитесь на интересующие вас теги, чтобы следить за новыми постами и быть в курсе событий.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *