Структура, доступная по имени атрибута или параметрам индекса - PullRequest
7 голосов
/ 25 марта 2010

Я очень плохо знаком с Python и пытаюсь выяснить, как создать объект, значения которого доступны либо по имени атрибута, либо по индексу. Например, способ os.stat () возвращает stat_result или pwd.getpwnam () возвращает struct_passwd.

Пытаясь понять это, я сталкивался только с реализациями C вышеупомянутых типов. Ничего конкретно в Python. Как Python является родным способом создания такого типа объектов?

Я прошу прощения, если это уже было широко освещено. В поисках ответа я, должно быть, упускаю некое фундаментальное понятие, которое мешает мне найти ответ.

Ответы [ 3 ]

5 голосов
/ 25 марта 2010

Python 2.6 представил collection.namedtuple , чтобы упростить эту задачу. В более старых версиях Python вы можете использовать именованный рецепт кортежа .

Цитирование непосредственно из документов:

>>> Point = namedtuple('Point', 'x y')
>>> p = Point(11, y=22)     # instantiate with positional or keyword arguments
>>> p[0] + p[1]             # indexable like the plain tuple (11, 22)
33
>>> x, y = p                # unpack like a regular tuple
>>> x, y
(11, 22)
>>> p.x + p.y               # fields also accessible by name
33
>>> p                       # readable __repr__ with a name=value style
Point(x=11, y=22)
3 голосов
/ 25 марта 2010

Вы не можете использовать ту же реализацию, что и объект результата os.stat () и других. Однако в Python 2.6 появилась новая фабричная функция, которая создает аналогичный тип данных с именем named tuple. Именованный кортеж - это кортеж, слоты которого также могут быть адресованы по имени. В соответствии с документацией именованный кортеж не должен требовать больше памяти, чем обычный кортеж, поскольку у него нет словаря для каждого экземпляра. Заводская функция подписи:

collections.namedtuple(typename, field_names[, verbose])  

Первый аргумент задает имя нового типа, второй аргумент - это строка (разделенная пробелами или запятыми), содержащая имена полей, и, наконец, если verbose равен true, функция factory также напечатает сгенерированный класс.

Пример

Предположим, у вас есть кортеж, содержащий имя пользователя и пароль. Для доступа к имени пользователя вы получаете элемент в нулевой позиции, а пароль - в первой позиции:

credential = ('joeuser', 'secret123')  
print 'Username:', credential[0]  
print 'Password:', credential[1]  

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

import collections  
# Create a new sub-tuple named Credential  
Credential = collections.namedtuple('Credential', 'username, password')  

credential = Credential(username='joeuser', password='secret123')  

print 'Username:', credential.username  
print 'Password:', credential.password  

Если вас интересует, как выглядит код для вновь созданного типа Credential, вы можете добавить verbose = True к списку аргументов при создании типа, в этом конкретном случае мы получим следующий вывод:

import collections  
Credential = collections.namedtuple('Credential', 'username, password', verbose=True)  

class Credential(tuple):                                       
    'Credential(username, password)'                       

    __slots__ = ()   

    _fields = ('username', 'password')   

    def __new__(_cls, username, password):  
        return _tuple.__new__(_cls, (username, password))   

    @classmethod  
    def _make(cls, iterable, new=tuple.__new__, len=len):  
        'Make a new Credential object from a sequence or iterable'  
        result = new(cls, iterable)                                 
        if len(result) != 2:                                        
            raise TypeError('Expected 2 arguments, got %d' % len(result))  
        return result  

    def __repr__(self):  
        return 'Credential(username=%r, password=%r)' % self  

    def _asdict(t):  
        'Return a new dict which maps field names to their values'  
        return {'username': t[0], 'password': t[1]}  

    def _replace(_self, **kwds):  
        'Return a new Credential object replacing specified fields with new values'  
        result = _self._make(map(kwds.pop, ('username', 'password'), _self))  
        if kwds:  
            raise ValueError('Got unexpected field names: %r' % kwds.keys())  
        return result  

    def __getnewargs__(self):  
        return tuple(self)  

    username = _property(_itemgetter(0))  
    password = _property(_itemgetter(1))  

Именованный кортеж не только предоставляет доступ к полям по имени, но также содержит вспомогательные функции, такие как функция _make (), которая помогает создавать экземпляр Credential из последовательности или итерации. Например:

cred_tuple = ('joeuser', 'secret123')  
credential = Credential._make(cred_tuple) 

Документация библиотеки python для namedtuple содержит больше информации и примеров кода, поэтому я советую вам взглянуть.

0 голосов
/ 25 марта 2010

объект, значения которого доступны либо по имени атрибута, либо по индексу

Я не уверен, что вы думаете об этом.

Коллекция, доступная по индексу, реализует __getitem__.

Коллекция, доступная по именам, реализует __getattr__ (или __getattribute__).

Вы можете реализовать оба без каких-либо проблем вообще. Или вы можете использовать namedtuple.

Чтобы упростить жизнь, вы можете расширить класс tuple, чтобы вам не пришлось реализовывать свой собственный __getitem__. Или вы можете определить обычный класс, который также имеет __getitem__, чтобы вам не приходилось связываться с __getattr__.

Например

>>> class Foo( object ):
...     def __init__( self, x, y, z ):
...         self.x= x
...         self.y= y
...         self.z= z
...     def __getitem__( self, index ):
...         return { 0: self.x, 1: self.y, 2: self.z }[index]
... 
>>> f= Foo(1,2,3)
>>> f.x
1
>>> f[0]
1
>>> f[1]
2
>>> f[2]
3
>>> f.y
2
...