最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - How to make an easily instantiable derivative attribute-only protocol class? - Stack Overflow

programmeradmin1浏览0评论

I have a Protocol subclass that defines objects with attributes from an external library:

class P(Protocol):
    val: int

For testing purposes, I want to turn this protocol class into something I can instantiate easily. However, when I try to turn it into a dataclass, an error pops up:

import dataclasses
from typing import Protocol

class P(Protocol):
    val: int

PInst = dataclasses.dataclass(P)

PInst(val=4)  # TypeError: Protocols cannot be instantiated

Is there an easy solution to use P to create a class that satifies its protocol and is instantiable, without redeclaring its attributes?

I have a Protocol subclass that defines objects with attributes from an external library:

class P(Protocol):
    val: int

For testing purposes, I want to turn this protocol class into something I can instantiate easily. However, when I try to turn it into a dataclass, an error pops up:

import dataclasses
from typing import Protocol

class P(Protocol):
    val: int

PInst = dataclasses.dataclass(P)

PInst(val=4)  # TypeError: Protocols cannot be instantiated

Is there an easy solution to use P to create a class that satifies its protocol and is instantiable, without redeclaring its attributes?

Share Improve this question edited Mar 13 at 9:33 Jan Šimbera asked Mar 13 at 8:39 Jan ŠimberaJan Šimbera 4883 silver badges17 bronze badges 6
  • 1 It's not entirely clear what kind of types P is supposed to match; @dataclasses.dataclass implicitly adds attributes (such as an __init__), and the type-checker sees these implicit attributes, so it's unclear from your definition whether items which are supposed to match P should also include matching implicit attributes or not. If they should include matching implicit attributes, then just ditch Protocol and subclass from P; otherwise I'd go for a class-based @typing.dataclass_transform and switch the base class of P based on compile-time constants. – dROOOze Commented Mar 13 at 9:16
  • @dROOOze I want to keep P a protocol; I edited the answer to explain that I need a derivative of P for testing purposes – Jan Šimbera Commented Mar 13 at 9:34
  • 1 Does this work for you? See mypy Playground. You can do something similar in pyright as well, with pyright's defineConstant configuration. – dROOOze Commented Mar 13 at 10:27
  • 1 I think you should see Nominal in dROOOze's example as a real case that you might encounter that follows the Protocol P, i.e. it is assignable to P and nothing you need to define in your code. – Daraan Commented Mar 13 at 13:43
  • 1 Right - as Daraan mentioned, Nominal in the playground example is a nominal type placeholder for something in your real code that is assignable to the structural type P. From your example, the point of using a protocol (structural type) is that arbitrary nominal types in your real code can match this structural type without subclassing from P. – dROOOze Commented Mar 13 at 17:45
 |  Show 1 more comment

1 Answer 1

Reset to default 2

You are asking for a non-protocol class derived from the attributes in your protocol class. They are stored in the annotations, which are accessible as typing.get_type_hints(P).

In my first try, I dynamically created that class with builtin type, passing it the protocol's type hints to define an equivalent non-protocol class, and pass that to dataclass. But it needed me to manually set dunder annotations before it could correctly create the init method.

But messing with internal attributes is a red flag telling me there should be an easier way. So I looked at the dataclasses public interface and found make_dataclass. You can just pass the annotations directly to its fields parameter.

from dataclasses import make_dataclass
from typing import Protocol, get_type_hints

class P(Protocol):
    val: int

DP = make_dataclass('DP', get_type_hints(P))
instance = DP(val=4)
print(instance)

output:

DP(val=4)

发布评论

评论列表(0)

  1. 暂无评论