内置函数到字典转换的特殊方法名

2024-04-23 15:46:30 发布

您现在位置:Python中文网/ 问答频道 /正文

我已经深入研究了Python类中的运算符重载和特殊方法,我注意到许多内置函数都有其等效的特殊方法名:

  • int(x)调用x.__int__()
  • next(x)在python2中调用x.__next__()x.next()

但是,一对函数,即tuple()dict(),没有任何等价函数。我知道还没有出现对这种特殊方法的需求,但是在某些情况下,对类调用的dict()转换方法可能是有用的。我该如何实现这一点?或者,你会怎么评价一个试图使用这种逻辑的人?你知道吗

# I think this is quite interesting, so I shall post my own implementation of it as well

Tags: 方法函数情况运算符逻辑内置dict评价
2条回答

以最简单的形式,我可以尝试检索obj.__dict__。我还创建了一个名为obj.__dictionary__的方法,它返回dict,但可以自定义返回的值和返回方式。我实现了一个小函数,它尝试调用obj.__dictionary__和其他方法的其他连续尝试:

def dct(obj=None, *args, **kwargs):
    if obj:
        try:
            r = obj.__dictionary__()
        except AttributeError:
            try:
                r = dict(obj)
            except TypeError:
                try:
                    r = obj.__dict__
                except AttributeError as e:
                    raise AttributeError(e) from None
                else:
                    return r
            else:
                return r
        else:
            return r
    else:
        return dict(*args, **kwargs)

为了演示,有一个小类封装了'Person'

class Person:
    def __init__(self, name, age, job):
        self.name = name
        self.age = age
        self.job = job

    def __dictionary__(self):
        return {'name': self.name,
                'age': self.age,
                'job': self.job,
                'title': '{} {}'.format(self.job, self.name)}

p = Person('bob', 42, 'farmer')

现在考虑在几个对象上调用我的自定义dct函数来测试它:

>>> dct(p)
{'name': 'bob', 'age': 42, 'job': 'farmer', 'title': 'farmer bob'}

>>> dct(item='apple', quantity=5)
{'item': 'apple', 'quantity': 5}

>>> dct([('username', 'admin'), ('password', '123')])
{'username': 'admin', 'password': '123'}

>>> dct({'a': 1, 'b': 2})
{'a': 1, 'b': 2}

正如你所看到的,它似乎工作在一个体面的方式。我知道对于我的简单的Person类演示,我可以简单地返回p.__dict__,但是我添加了一个额外的p.title属性,该属性只以dict形式存在,以表明这支持额外的定制。另外,如果我用__slots__定义了Person类,那么dct(p)调用将失败

AttributeError: 'Person' object has no attribute '__dict__'

显然,我可以扩展try/except块来尝试访问__slots__,但这并不是我想说的重点:如果我在自定义__dictionary__方法中定义了以下内容,代码将 按预期工作:

def __dictionary__(self):
    return {name: getattr(self, name, None)
            for name in self.__slots__}

选项1:__iter__

转换为tupledict,或任何采用iterable的类型,都依赖于__iter__方法。你知道吗

class ListOfKeys():
    def __init__(self, lst):
        self.lst = lst

    def __iter__(self):
        for k in self.lst:
            yield (k, None)

lok = ListOfKeys([1, 2, 3])
d = dict(lok)

print(d) # {1: None, 2: None, 3: None}

这同样适用于元组。你知道吗

t = tuple(lok)

print(t) # ((1, None), (2, None), (3, None))

选项2:keys__getitem__

或者,要转换为dict,可以同时实现keys__getitem__。你知道吗

class ListOfKeys():
    def __init__(self, lst):
        self.lst = lst

    def keys(self):
        yield from self.lst

    def __getitem__(self, item):
        return None

lok = ListOfKeys([1, 2, 3])
d = dict(lok)

print(d) # {1: None, 2: None, 3: None}

选项3:都支持多种类型

最后,如果希望类具有不同的行为来强制转换为dicttuple,下面的示例演示了dict将优先考虑keys__getitem__解决方案。你知道吗

class Foo:
    def __iter__(self):
        yield 1

    def keys(self):
        yield 2

    def __getitem__(self, item):
        return 3

print(dict(Foo())) # {2: 3}
print(tuple(Foo())) # (1,)

相关问题 更多 >