7

我对 Python 非常陌生,并试图弄清楚如何创建一个具有可通过属性名称或索引访问的值的对象。例如,os.stat() 返回 stat_result 或 pwd.getpwnam() 返回 struct_passwd 的方式。

在试图弄清楚时,我只遇到过上述类型的 C 实现。在 Python 中没有什么特别的。创建这种对象的 Python 原生方式是什么?

如果这已经被广泛报道,我深表歉意。在寻找答案时,我一定错过了一些使我无法找到答案的基本概念。

4

3 回答 3

5

Python 2.6 引入了collections.namedtuple来简化这个过程。对于较旧的 Python 版本,您可以使用命名的 tuple recipe

直接从文档引用:

>>> 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)
于 2010-03-25T02:01:22.873 回答
3

您不能使用与 os.stat() 和其他的结果对象相同的实现。然而,Python 2.6 有一个新的工厂函数,它创建了一个类似的数据类型,称为命名元组。命名元组是一个元组,其槽也可以按名称寻址。根据文档,命名元组不应该比常规元组需要更多的内存,因为它们没有每个实例的字典。工厂函数签名是:

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

第一个参数指定新类型的名称,第二个参数是包含字段名称的字符串(空格或逗号分隔),最后,如果verbose 为真,工厂函数还将打印生成的类。

例子

假设您有一个包含用户名和密码的元组。要访问用户名,您将在位置 0 获得项目,并在位置 1 访问密码:

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-type 的代码外观感兴趣,您可以在创建类型时将 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) 

namedtuple 的 python 库文档有更多的信息和代码示例,所以我建议你看一看。

于 2010-03-25T06:21:57.860 回答
0

具有可通过属性名称或索引访问的值的对象

我不确定您对此有何难处。

可由索引实现访问的集合__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
于 2010-03-25T02:54:57.787 回答