You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现类属性API内公开外私有,及公共类内部专属实例化?

解决方案

一、包内访问私有属性(对外隐藏,类型检查合规)

1. 利用模块__all__与内部访问约定

单下划线开头的属性(如_context)是Python约定的模块级私有成员,同包内的模块访问这类属性时,mypy、Pylance等类型检查器默认不会报错。只需在类定义的模块中通过__all__控制对外导出的内容,仅暴露Foo、Bar等公共类,隐藏内部细节:

# core.py
__all__ = ["Foo", "Bar"]

class Context:
    pass

class Foo(Generic[T]):
    _context: Context

class Bar(Generic[T]):
    _context: Context

# operations.py(同包内模块)
from core import Foo, Bar

def func_1(foo: Foo[str]) -> int:
    # 类型检查器允许同包内访问单下划线私有属性
    do_something(foo._context)
    return 0

2. 使用typing.PrivateAttr(Python 3.10+)

用PrivateAttr明确标记属性为类私有成员,类型检查器会识别其私有特性,内部访问合法,外部访问会触发类型警告:

from typing import Generic, TypeVar, PrivateAttr

T = TypeVar('T')
class Context:
    pass

class Foo(Generic[T]):
    _context: Context = PrivateAttr()

class Bar(Generic[T]):
    _context: Context = PrivateAttr()

def func_1(foo: Foo[str]) -> int:
    # 类型检查器认可内部对PrivateAttr的访问
    process_context(foo._context)
    return 0

3. 内部协议标注(跨模块严格类型检查)

若操作函数与类不在同一模块,可定义仅内部可见的协议,声明允许访问_context,让操作函数的参数标注为该协议,同时通过__all__隐藏协议:

# core.py
__all__ = ["Foo", "Bar"]

from typing import Generic, TypeVar, Protocol

T = TypeVar('T')
class Context:
    pass

# 仅内部可见的协议,声明拥有_context属性
class _HasContext(Protocol[T]):
    _context: Context

class Foo(Generic[T], _HasContext[T]):
    _context: Context

class Bar(Generic[T], _HasContext[T]):
    _context: Context

# operations.py
from core import _HasContext, Foo, Bar

def func_1(foo: _HasContext[str]) -> int:
    # 类型检查器认可协议中的属性访问
    handle_context(foo._context)
    return 0

二、阻止外部实例化公共类(内部可实例化)

1. 私有构造函数+内部工厂方法

将类的__init__设为双下划线私有(名称修饰后外部无法访问),同时提供内部可见的工厂方法创建实例,通过__all__隐藏工厂方法:

# core.py
__all__ = ["Foo", "Bar"]

from typing import Generic, TypeVar

T = TypeVar('T')
class Context:
    pass

class Foo(Generic[T]):
    _context: Context

    # 双下划线构造函数,外部无法直接调用
    def __init__(self, context: Context):
        self._context = context

    # 内部工厂方法,对外不导出
    @classmethod
    def _create(cls, context: Context) -> "Foo[T]":
        return cls(context)

# operations.py
from core import Foo, Context

def create_string_foo() -> Foo[str]:
    ctx = Context()
    # 内部调用工厂方法创建实例
    return Foo._create(ctx)

2. 调用栈检查(严格限制实例化来源)

在__init__中检查调用者模块,仅允许内部包内模块实例化类:

import inspect
from typing import Generic, TypeVar

T = TypeVar('T')
class Context:
    pass

class Foo(Generic[T]):
    _context: Context

    def __init__(self, context: Context):
        # 获取调用者模块名
        caller_module = inspect.stack()[1].module.__name__
        # 限制仅内部包(如your_package)的模块可实例化
        if not caller_module.startswith('your_package.'):
            raise RuntimeError("Cannot instantiate Foo directly. Use internal factory methods.")
        self._context = context

3. 元类控制(批量限制多类实例化)

通过元类拦截实例化请求,统一控制所有标记类的实例化权限:

import inspect
from typing import Generic, TypeVar, Type

T = TypeVar('T')
class Context:
    pass

class _InternalOnlyMeta(type):
    def __call__(cls, *args, **kwargs):
        caller_module = inspect.stack()[1].module.__name__
        if not caller_module.startswith('your_package.'):
            raise RuntimeError("Cannot instantiate this class directly.")
        return super().__call__(*args, **kwargs)

class Foo(Generic[T], metaclass=_InternalOnlyMeta):
    _context: Context

    def __init__(self, context: Context):
        self._context = context

class Bar(Generic[T], metaclass=_InternalOnlyMeta):
    _context: Context

    def __init__(self, context: Context):
        self._context = context

内容的提问来源于stack exchange,提问作者person17381

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 07:05:56