为什么 Python 没有混合 getattr + __getitem__ 内置?

发布于 2024-11-24 08:31:58 字数 1276 浏览 1 评论 0原文

我有接受字典或其他对象以及从这些对象中获取的“字段”名称的方法。如果对象是字典,则该方法使用 __getitem__ 来检索命名键,否则使用 getattr 来检索命名属性。这在网络模板语言中很常见。例如,在 Chameleon 模板中,您可能有:

<p tal:content="foo.keyname">Stuff goes here</p>

如果您将 foo 作为字典传入就像 {'keyname':'bar'} 一样,然后 foo.keyname 获取 'keyname' 键来获取 'bar'。如果 foo 是如下类的实例:

class Foo(object):
    keyname = 'baz'

foo.keynamekeyname 属性中获取值。 Chameleon 本身实现了该函数(在 chameleon.py26 模块中),如下所示:

def lookup_attr(obj, key):
    try:
        return getattr(obj, key)
    except AttributeError as exc:
        try:
            get = obj.__getitem__
        except AttributeError:
            raise exc
        try:
            return get(key)
        except KeyError:
            raise exc

我已在 我自己的包就像:

try:
    value = obj[attribute]
except (KeyError, TypeError):
    value = getattr(obj, attribute)

事实是,这是一个非常常见的模式。我在很多模块中都看到过这种方法或与其非常相似的方法。那么,为什么语言的核心或至少核心模块之一没有类似的东西呢?如果做不到这一点,是否有一个明确的方法应该如何编写?

I have methods that accept dicts or other objects and the names of "fields" to fetch from those objects. If the object is a dict then the method uses __getitem__ to retrieve the named key, or else it uses getattr to retrieve the named attribute. This is pretty common in web templating languages. For example, in a Chameleon template you might have:

<p tal:content="foo.keyname">Stuff goes here</p>

If you pass in foo as a dict like {'keyname':'bar'}, then foo.keyname fetches the 'keyname' key to get 'bar'. If foo is an instance of a class like:

class Foo(object):
    keyname = 'baz'

then foo.keyname fetches the value from the keyname attribute. Chameleon itself implements that function (in the chameleon.py26 module) like this:

def lookup_attr(obj, key):
    try:
        return getattr(obj, key)
    except AttributeError as exc:
        try:
            get = obj.__getitem__
        except AttributeError:
            raise exc
        try:
            return get(key)
        except KeyError:
            raise exc

I've implemented it in my own package like:

try:
    value = obj[attribute]
except (KeyError, TypeError):
    value = getattr(obj, attribute)

The thing is, that's a pretty common pattern. I've seen that method or one awfully similar to it in a lot of modules. So why isn't something like it in the core of the language, or at least in one of the core modules? Failing that, is there a definitive way of how that could should be written?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

优雅的叶子 2024-12-01 08:31:58

我读了一半你的问题,写了下面的内容,然后重读你的问题,意识到我回答了一个略有不同的问题。但我想,经过一番整理,下面的内容实际上仍然提供了答案。如果您不这么认为,请假装您问了这个更一般的问题,我认为该问题包括您的子问题:

“为什么 Python 不提供任何内置方法来将属性和物品视为可互换的吗?”


我对这个问题进行了相当多的思考,我认为答案很简单。创建容器类型时,区分属性项目非常重要。任何相当完善的容器类型都将具有许多属性(通常但不总是方法),使其能够以优雅的方式管理其内容。例如,字典有 itemsvalueskeysiterkeys 等。这些属性均使用 . 表示法进行访问。另一方面,项目是使用 [] 表示法访问的。所以不可能有碰撞。

当您使用 . 表示法启用项目访问时会发生什么?突然之间,你就有了重叠的命名空间。现在如何处理碰撞?如果您对字典进行子类化并赋予它此功能,那么您通常不能使用像 items 这样的键,或者您必须创建某种名称空间层次结构。第一个选项创建了一个繁琐、难以遵循且难以执行的规则。第二个选项会产生令人烦恼的复杂性,但无法完全解决冲突问题,因为您仍然需要有一个替代接口来指定您是想要 items 项目还是 items属性。

现在,对于某些非常原始的类型,这是可以接受的。例如,这可能就是标准库中存在 namedtuple 的原因。 (但请注意,namedtuple 也存在这些问题,这可能就是为什么它被实现为工厂函数(防止继承)并使用奇怪的私有方法名称,如 _asdict .)

创建没有(公共)属性的 object 子类并在其上使用 setattr 也非常非常容易。重写 __getitem__、__setitem____delitem__ 来调用 __getattribute____setattr__ 甚至非常容易code> 和 __delattr__,这样项目访问就变成了 getattr()setattr() 的语法糖, (尽管这有点可疑,因为它会产生一些意想不到的行为。)

但是对于您希望能够扩展和继承的任何类型的开发良好的容器类,添加新的、有用的属性,__getattr__坦率地说,+ __getitem__ 混合体将是一个巨大的 PITA。

I sort of half-read your question, wrote the below, and then reread your question and realized I had answered a subtly different question. But I think the below actually still provides an answer after a sort. If you don't think so, pretend instead that you had asked this more general question, which I think includes yours as a sub-question:

"Why doesn't Python provide any built-in way to treat attributes and items as interchangable?"


I've given a fair bit of thought to this question, and I think the answer is very simple. When you create a container type, it's very important to distinguish between attributes and items. Any reasonably well-developed container type will have a number of attributes -- often though not always methods -- that enable it to manage its contents in graceful ways. So for example, a dict has items, values, keys, iterkeys and so on. These attributes are all accessed using . notation. Items, on the other hand, are accessed using [] notation. So there can be no collisions.

What happens when you enable item access using . notation? Suddenly you have overlapping namespaces. How do you handle collisions now? If you subclass a dict and give it this functionality, either you can't use keys like items as a rule, or you have to create some kind of namespace hierarchy. The first option creates a rule that is onerous, hard to follow, and hard to enforce. The second option creates an annoying amount of complexity, without fully resolving the collision problem, since you still have to have an alternative interface to specify whether you want items the item or items the attribute.

Now, for certain kinds of very primitive types, this is acceptable. That's probably why there's namedtuple in the standard library, for example. (But note that namedtuple is subject to these very problems, which is probably why it was implemented as a factory function (prevents inheritance) and uses weird, private method names like _asdict.)

It's also very, very, very easy to create a subclass of object with no (public) attributes and use setattr on it. It's even pretty easy to override __getitem__, __setitem__, and __delitem__ to invoke __getattribute__, __setattr__ and __delattr__, so that item access just becomes syntactic sugar for getattr(), setattr(), etc. (Though that's a bit more questionable since it creates somewhat unexpected behavior.)

But for any kind of well-developed container class that you want to be able to expand and inherit from, adding new, useful attributes, a __getattr__ + __getitem__ hybrid would be, frankly, an enormous PITA.

携余温的黄昏 2024-12-01 08:31:58

python标准库中最接近的东西是namedtuple(),http:// /docs.python.org/dev/library/collections.html#collections.namedtuple

Foo = namedtuple('Foo', ['key', 'attribute'])
foo = Foo(5, attribute=13)
print foo[1]
print foo.key

或者您可以轻松定义自己的类型,该类型始终实际存储到其字典中,但允许出现属性设置和获取:

class MyDict(dict):
    def __getattr__(self, attr):
        return self[attr]
    def __setattr__(self, attr, value):
        self[attr] = value

d = MyDict()

d.a = 3
d[3] = 'a'
print(d['a']) # 3
print(d[3]) # 'a'
print(d['b']) # Returns a keyerror

但是不要'要做d.3 因为这是一个语法错误。当然,还有更复杂的方法可以制作这样的混合存储类型,请在网上搜索许多示例。

至于如何检查两者,变色龙方式看起来很彻底。当谈到“为什么没有一种方法可以在标准库中同时完成这两项工作”时,这是因为歧义是不好的。是的,我们在 python 中有鸭子打字和所有其他类型的伪装,无论如何,类实际上只是字典,但在某些时候,我们希望从像字典或列表这样的容器中获得与我们想要从类中获得的功能不同的功能,其方法解析顺序、覆盖等

The closest thing in the python standard library is a namedtuple(), http://docs.python.org/dev/library/collections.html#collections.namedtuple

Foo = namedtuple('Foo', ['key', 'attribute'])
foo = Foo(5, attribute=13)
print foo[1]
print foo.key

Or you can easily define your own type that always actually stores into it's dict but allows the appearance of attribute setting and getting:

class MyDict(dict):
    def __getattr__(self, attr):
        return self[attr]
    def __setattr__(self, attr, value):
        self[attr] = value

d = MyDict()

d.a = 3
d[3] = 'a'
print(d['a']) # 3
print(d[3]) # 'a'
print(d['b']) # Returns a keyerror

But don't do d.3 because that's a syntax error. There are of course more complicated ways out there of making a hybrid storage type like this, search the web for many examples.

As far as how to check both, the Chameleon way looks thorough. When it comes to 'why isn't there a way to do both in the standard library' it's because ambiguity is BAD. Yes, we have ducktyping and all other kinds of masquerading in python, and classes are really just dictionaries anyway, but at some point we want different functionality from a container like a dict or list than we want from a class, with it's method resolution order, overriding, etc.

翻了热茶 2024-12-01 08:31:58

您可以非常轻松地编写自己的 dict 子类,该子类本身就以这种方式运行。一个最小的实现,我喜欢称之为属性“堆”,是这样的:

class Pile(dict):
    # raise AttributeError for missing key here to fulfill API
    def __getattr__(self, key):
        if key in self:
            return self[key]
        else:
            raise AttributeError(key)
    def __setattr__(self, key, value):
        self[key] = value

不幸的是,如果您需要能够处理传递给您的字典或包含属性的对象,而不是从开始,这没有帮助。

在你的情况下,我可能会使用与你所拥有的非常相似的东西,除了将其分解为一个函数,这样我就不必一直重复它。

You can pretty easily write your own dict subclass that natively behaves this way. A minimal implementation, which I like to call a "pile" of attributes, is like so:

class Pile(dict):
    # raise AttributeError for missing key here to fulfill API
    def __getattr__(self, key):
        if key in self:
            return self[key]
        else:
            raise AttributeError(key)
    def __setattr__(self, key, value):
        self[key] = value

Unfortunately if you need to be able to deal with either dictionaries or attribute-laden objects passed to you, rather than having control of the object from the beginning, this won't help.

In your situation I would probably use something very much like what you have, except break it out into a function so I don't have to repeat it all the time.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文