Feature Request: Adding Way to Annotate Class Variables Distinct from Instance Variables

Hello folks, I am Chihiro, a.k.a. Frodo821, and this is my first post to this group. I searched for a way to annotate both class variables and instance variables with different types and concluded that there is currently no way to do this. For example, what I want to: ``` class Foo: variable_both_class_and_instance = Field(desc="descriptive string") def __init__(self, value: int): self.variable_both_class_and_instance = value assert isinstance(Foo.variable_both_class_and_instance, Field) assert isinstance(Foo().variable_both_class_and_instance, int) ``` In this example, I want to annotate `Foo.variable_both_class_and_instance` with `Field` when it is accessed as a class variable. On the other hand, I want to annotate `Foo.variable_both_class_and_instance` with `int` when it is accessed as an instance variable. I don't have any smart ideas to accomplish this, but I think `typing.ClassVar` could be extended this like: ``` class Foo: variable_both_class_and_instance: Union[ClassVar[Field], int] = Field(desc="descriptive string") def __init__(self, value: int): self.variable_both_class_and_instance = value assert isinstance(Foo.variable_both_class_and_instance, Field) assert isinstance(Foo().variable_both_class_and_instance, int) ``` Do you have any ideas or comments about this?

Well, the first comment is that this isn't really the best list to ask such questions on, since it was created for the Python developers to discuss the development of the language and its implementation. Further, such discussions nowadays take place on discuss.python.org, and you can find more information at https://www.python.org/community/lists/. However, I simplified your program a little, and would observe that the following program raises no AssertionErrors under Python 3.10 and 3.11. class Foo: variable_both_class_and_instance: str | int = "descriptive string" def __init__(self, value: int): assert isinstance(self.variable_both_class_and_instance, str) self.variable_both_class_and_instance = value assert isinstance(self.variable_both_class_and_instance, int) assert isinstance(Foo.variable_both_class_and_instance, str) assert isinstance(Foo(42).variable_both_class_and_instance, int) Union is now outdated, since we can use alternation (|) to offer alternative types. It's therefore not obvious to me from your email why ClassVar would need any modification, or even why it needs to be used in your example. Perhaps I've missed your point? Kind regards, Steve On Wed, Dec 21, 2022 at 4:15 PM <sakaic2003@gmail.com> wrote:

First: this is Python-dev, which is not really the best palce for this kind of question. I'd try: https://discuss.python.org/ Though interestingly, I don't see a Typing topic --maybe I missed it. Or this list: https://mail.python.org/archives/list/typing-sig@python.org/ But a couple thoughts: 1) I'm a bit confused -- you haven't done a type annotation for the class variable, only for the method argument. So not sure what you really intend here. 2) isinstance() checks run-time types -- it has nothing to do with type annotations. So I'm not totally clear on what you want here. But from your description, it sounds like you want to annotate the type of a class attribute and and an instance attribute with two different types. I have no idea if that's possible, but it does seem tobe be a "bad idea" -- and contrary to the goal of static typing. Annotating the type of a class attribute is setting it for instance attributes as well -- and that is intended behavior. HTH, -CHB On Wed, Dec 21, 2022 at 8:17 AM <sakaic2003@gmail.com> wrote:
-- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython

OK, I missed out on typing-sig@python.org, and now I know this thread is off-topic on python-dev@python.org. A bit more clear example of what I want to do is: ``` class Field: def __init__(self, desc: str) -> None: self.desc = desc class FooMetaType: variable_both_class_and_instance: Field def __call__(self, value: int) -> 'Foo': ... class Foo: variable_both_class_and_instance: int def __init__(self, value: int) -> None: self.variable_both_class_and_instance = value Foo: FooMetaType Foo.variable_both_class_and_instance = Field(desc="descriptive string") instance = Foo(5) print(instance.variable_both_class_and_instance) ``` Static typing on this example doesn't work correctly as expected because it does not allow overwriting another type over a class declaration. My point on the example is the tangible value should be assigned to the instance variables. Still, on the other hand, I want to give a value denoting the variable's metadata to the class variable. From my understanding, these variables, like this `variable_both_class_and_instance` in the example, cannot be correctly typed. Thank you for your kindness, I'll repost it to typing-sig@python.org.

Well, the first comment is that this isn't really the best list to ask such questions on, since it was created for the Python developers to discuss the development of the language and its implementation. Further, such discussions nowadays take place on discuss.python.org, and you can find more information at https://www.python.org/community/lists/. However, I simplified your program a little, and would observe that the following program raises no AssertionErrors under Python 3.10 and 3.11. class Foo: variable_both_class_and_instance: str | int = "descriptive string" def __init__(self, value: int): assert isinstance(self.variable_both_class_and_instance, str) self.variable_both_class_and_instance = value assert isinstance(self.variable_both_class_and_instance, int) assert isinstance(Foo.variable_both_class_and_instance, str) assert isinstance(Foo(42).variable_both_class_and_instance, int) Union is now outdated, since we can use alternation (|) to offer alternative types. It's therefore not obvious to me from your email why ClassVar would need any modification, or even why it needs to be used in your example. Perhaps I've missed your point? Kind regards, Steve On Wed, Dec 21, 2022 at 4:15 PM <sakaic2003@gmail.com> wrote:

First: this is Python-dev, which is not really the best palce for this kind of question. I'd try: https://discuss.python.org/ Though interestingly, I don't see a Typing topic --maybe I missed it. Or this list: https://mail.python.org/archives/list/typing-sig@python.org/ But a couple thoughts: 1) I'm a bit confused -- you haven't done a type annotation for the class variable, only for the method argument. So not sure what you really intend here. 2) isinstance() checks run-time types -- it has nothing to do with type annotations. So I'm not totally clear on what you want here. But from your description, it sounds like you want to annotate the type of a class attribute and and an instance attribute with two different types. I have no idea if that's possible, but it does seem tobe be a "bad idea" -- and contrary to the goal of static typing. Annotating the type of a class attribute is setting it for instance attributes as well -- and that is intended behavior. HTH, -CHB On Wed, Dec 21, 2022 at 8:17 AM <sakaic2003@gmail.com> wrote:
-- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython

OK, I missed out on typing-sig@python.org, and now I know this thread is off-topic on python-dev@python.org. A bit more clear example of what I want to do is: ``` class Field: def __init__(self, desc: str) -> None: self.desc = desc class FooMetaType: variable_both_class_and_instance: Field def __call__(self, value: int) -> 'Foo': ... class Foo: variable_both_class_and_instance: int def __init__(self, value: int) -> None: self.variable_both_class_and_instance = value Foo: FooMetaType Foo.variable_both_class_and_instance = Field(desc="descriptive string") instance = Foo(5) print(instance.variable_both_class_and_instance) ``` Static typing on this example doesn't work correctly as expected because it does not allow overwriting another type over a class declaration. My point on the example is the tangible value should be assigned to the instance variables. Still, on the other hand, I want to give a value denoting the variable's metadata to the class variable. From my understanding, these variables, like this `variable_both_class_and_instance` in the example, cannot be correctly typed. Thank you for your kindness, I'll repost it to typing-sig@python.org.
participants (4)
-
Chihiro Sakai
-
Christopher Barker
-
sakaic2003@gmail.com
-
Steve Holden