通过 __getattr__ 进行动态属性的 Python 3 类型提示

问题描述 投票:0回答:2

要包装模块的所有函数调用并通过包装类的

__getattr__
方法访问它,我尝试使用
typing
库,但我不知道如何正确执行此操作。

import interface

"""
>>> print(interface.__all__)
['execute_foo_operation', ...]
"""

class InterfaceWrapper(object):
    def __init__(self, job_queue):
        self.job_queue = job_queue
        self.callbacks = []

    def __getattr__(self, name):
        func = getattr(interface, name)
        return functools.partial(self._wrapper, func)

    def _wrapper(self, func, *args, **kwargs):
        job = func(*args, **kwargs)
        self.job_queue.push(job)
        for callback in self.callbacks:
            callback(job)
        return job

    def register_callback(self, callback):
        self.callbacks.append(callback)


class Operator(object):
    def __init__(self, job_queue):
        self.interface = InterfaceWrapper(job_queue)

    def after_queuing(self):
        # do something

    def execute_foo_operation(self):
        self.interface.register_callback(self.after_queuing)
        self.interface.execute_foo_operation()  # unresolved attribute

任何人都可以指导我的代码正常工作吗?

python python-3.x python-decorators type-hinting
2个回答
0
投票

通常,

__getattr__
可以返回任何内容。所以你可以使用 Typing.Any

From Typing import Any

def __getattr__(self, name: str) -> Any:
    ...

但看起来你的

__getattr__
的实现是返回一个可调用的

From Typing import Callable

def __getattr__(self, name: str) -> Callable:
    ...

0
投票

如果我理解正确的话,您的请求本质上是要求 statical 类型检查器推断 __getattr__ 返回的

dynamical
属性的引用,这对于大多数类型检查器来说可能相当具有挑战性。事实上,对于 PyCharm (>=2020.3.5 (203.7717.81)),如果在
class
中定义了 __getattr__,则禁用对未解析属性的检查。

存在一种解决方法,但要求您不使用

__getattr__
访问属性,而是事先通过
typing.Literal
手动静态定义所有属性名称,并使用这些文字在下面的自定义定义
name
方法中注释
get_attr
:

import functools
from typing import Literal
supported = Literal[Literal['a'], Literal['b']]


class InterfaceWrapper(object):
    def __init__(self, job_queue):
        self.job_queue = job_queue
        self.callbacks = []

    def get_attr(self, name: supported):
        func = getattr(interface, name)
        return functools.partial(self._wrapper, func)

    def _wrapper(self, func, *args, **kwargs):
        job = func(*args, **kwargs)
        self.job_queue.push(job)
        for callback in self.callbacks:
            callback(job)
        return job

    def register_callback(self, callback):
        self.callbacks.append(callback)

class Operator(object):
    interface: InterfaceWrapper

    def __init__(self, job_queue):
        self.interface: InterfaceWrapper = InterfaceWrapper(job_queue)

    def after_queuing(self):
        # do something
        ...

    def execute_foo_operation(self):
        # ... do something
        self.interface.get_attr('a')()

这里,如果您调用 self.interface.get_attr('c'),它将报告预期为 'a' 和 'b',但得到的是 'c'。

如果您只想通过

__getattr__
(interface.attr_name) 访问方法或属性,那么您至少需要在 InterfaceWrapper 中的某个位置静态定义接口的属性,例如,定义指向接口对象中的相应字段。

© www.soinside.com 2019 - 2024. All rights reserved.