如何检查 class 是否具有带有“'attr' in instance”语法的属性

How to check if a class has an attribute with `'attr' in instance` syntax

我希望能够使用 'attr' in instance 语法来检查我的数据类是否具有指定的属性,但我似乎无法使其工作。

我想要的是与此示例相同的行为 pandas

import pandas as pd

df = pd.DataFrame(columns=['a', 'b', 'c'])
print('a' in df)
True

但仅针对自定义数据类

from dataclasses import dataclass

@dataclass
class User:
    email: int
    password: str
    blocked_at: float = None
    
    def __getitem__(self, item):
        return getattr(self, item)

user = User('email@test.com', 'password')

print(user['email'])

'email' in user
email@test.com

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Input In [35], in <cell line: 1>()
----> 1 'email' in user in User.__getitem__(self, item)
      7 def __getitem__(self, item):
----> 8     return getattr(self, item)

TypeError: getattr(): attribute name must be string

这是因为您没有定义正确的钩子。您要实施 __contains__ method.

因为你没有,in 运算符切换到回退模式:迭代对象 就好像它是一个序列,所以它尝试 object[0],然后是 object[1],等等,直到遇到 IndexError 或找到与您要测试的值相等的值。因此,例外,因为 item0.

使用 hasattr 而不是 getattr,因为您想要 布尔值 结果。对于您的 __getitem__,您要确保将 AttributeError 异常转换为 KeyError 异常,以保持界面一致:

from __future__ import annotations
from dataclasses import dataclass

@dataclass
class User:
    email: int
    password: str
    blocked_at: float = None

    def __getitem__(self, item: str) -> str | int | float | None:
        try:
            return getattr(self, item)
        except AttributeError:
            raise KeyError(item) from None
    
    def __contains__(self, item: str) -> bool:
        return hasattr(self, item)

演示:

>>> user = User('email@test.com', 'password')
>>> print(user['email'])
email@test.com
>>> 'email' in user
True
>>> user["nonesuch"]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 12, in __getitem__
KeyError: 'nonesuch'

有关 in 如何回退到迭代的详细信息,请参阅 Python reference section on membership test operations

For user-defined classes which do not define __contains__() but do define __iter__(), x in y is True if some value z, for which the expression x is z or x == z is true, is produced while iterating over y. If an exception is raised during the iteration, it is as if in raised that exception.

Lastly, the old-style iteration protocol is tried: if a class defines __getitem__(), x in y is True if and only if there is a non-negative integer index i such that x is y[i] or x == y[i], and no lower integer index raises the IndexError exception. (If any other exception is raised, it is as if in raised that exception).