You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Distinguish between attributes of a class and attributes of its instances #1097
This is how mypy currently behaves by design, but the behavior is clearly a compromise. To distinguish between class and instance attributes, we could do something like this:
Attributes only initialized via self get an instance attribute flag. These attributes can't be accessed via the class object (neither read or set).
The instance attribute flags are normally inherited in derived classes.
It's okay to assign to an inherited instance attribute in a class body. In this case the attribute loses the instance attribute flag in the derived class.
Attributes without the flag (i.e. those assigned to in the class body) can be used as either instance or class attributes. They behave identically to how all attributes are currently treated by mypy.
One thing you can do to help would be to support PEP 526, in its tracker item at python/typing#258 and its draft at https://github.com/phouse512/peps/commits/pep-0526. And maybe ClassVar proposed there can be backported? But I guess first and foremost mypy's internals need to be redesigned to be able to make the distinction.
There's a proposed new way of annotation class variables: python/typing#258
On pre-3.6 Python it would look like this:
from typing import ClassVar
class A:
x = 0 # type: ClassVar[int] # Class variable
y = 0 # Instance variable with default in class body
A().x = 3 # Error: "x' is a class variable
This wouldn't immediately help with the original issue, though, and the original proposal that I discussed above is still relevant. We've made no progress on that.
@oleiade If you are interested in trying to implement instance-only attribute detection, I'm happy to give some ideas about how to do it -- or if you want to bounce ideas off me, please go ahead :-)
Historically classvars was used to to define runtime types of instances with the same name in ORMs, forms, validatiors.
As an example SQLAlchemy DeclarativeBase mentioned in #974
class User(Base):
name = ...
User().name: str but User.name: InstrumentedAttribute and it is used as filter:
session.query(User).filter(User.name.like('...'))
I don't know a details of how hard it could be implemented but at first glance it could be expressed as type without new syntax. name: ClassInstanceVar[classvartype, instancevartype]
UPD:
Actually according PEP mypy should not treat User.name as str if it's defined as name: str because its instance definition anyway, and currently it does.
Reacted by Tuukka Mustonen, Dan Palmer, wouter bolsterlee and Maciej Olko
Actually according PEP mypy should not treat User.name as str if it's defined as name: str because its instance definition anyway, and currently it does.
Well if you just put name: str in the class I think mypy is right in inferring both User.name and User().name as type str. It is quite similar to putting name: str = '' in the class -- mypy doesn't care whether the value is initialized. And this is a common use case that we don't want to break.
For the actual example using InstrumentedAttribute maybe the solution is to make that class implement the descriptor protocol (__get__), at least in the stub. Then mypy uses the __get__ return type for the instance variable type.
FWIW, Kythe takes a similar attitude to instance and class variables for cross-referencing source files (and not just for Python) -- both instance and class variables are treated as the same.
changed the title [-]MyPy doesn't seem to distinguish between attributes of a class and attributes of its instances[/-][+]Distinguish between attributes of a class and attributes of its instances[/+]on May 18, 2018
Activity
JukkaL commentedon Dec 28, 2015
This is how mypy currently behaves by design, but the behavior is clearly a compromise. To distinguish between class and instance attributes, we could do something like this:
selfget an instance attribute flag. These attributes can't be accessed via the class object (neither read or set).oleiade commentedon Aug 28, 2016
I very often hit this limitation. Has been any advancement made on the topic? How can I help making this move forward?
Thanks :-)
gvanrossum commentedon Aug 28, 2016
One thing you can do to help would be to support PEP 526, in its tracker item at python/typing#258 and its draft at https://github.com/phouse512/peps/commits/pep-0526. And maybe
ClassVarproposed there can be backported? But I guess first and foremost mypy's internals need to be redesigned to be able to make the distinction.JukkaL commentedon Aug 28, 2016
There's a proposed new way of annotation class variables: python/typing#258
On pre-3.6 Python it would look like this:
This wouldn't immediately help with the original issue, though, and the original proposal that I discussed above is still relevant. We've made no progress on that.
@oleiade If you are interested in trying to implement instance-only attribute detection, I'm happy to give some ideas about how to do it -- or if you want to bounce ideas off me, please go ahead :-)
enomado commentedon Mar 13, 2018
Historically classvars was used to to define runtime types of instances with the same name in ORMs, forms, validatiors.
As an example
SQLAlchemy DeclarativeBasementioned in #974User().name: strbutUser.name: InstrumentedAttributeand it is used as filter:I don't know a details of how hard it could be implemented but at first glance it could be expressed as type without new syntax.
name: ClassInstanceVar[classvartype, instancevartype]UPD:
Actually according PEP mypy should not treat User.name as
strif it's defined asname: strbecause its instance definition anyway, and currently it does.mtvx commentedon Apr 6, 2018
@enomado I was actually just recently asking something similar for peewee ORM in coleifer/peewee#1559.
Maybe
classvartypecould be even inferred as in:So you mean with just:
mypy will infer
Anytype forUser().name?gvanrossum commentedon Apr 6, 2018
No:
Gives
enomado commentedon Apr 6, 2018
All this issue is about that typing system should
feela difference, but currently it does not.buildins.strin all casesgvanrossum commentedon Apr 6, 2018
Well if you just put
name: strin the class I think mypy is right in inferring bothUser.nameandUser().nameas typestr. It is quite similar to puttingname: str = ''in the class -- mypy doesn't care whether the value is initialized. And this is a common use case that we don't want to break.For the actual example using
InstrumentedAttributemaybe the solution is to make that class implement the descriptor protocol (__get__), at least in the stub. Then mypy uses the__get__return type for the instance variable type.kamahen commentedon Apr 6, 2018
FWIW, Kythe takes a similar attitude to instance and class variables for cross-referencing source files (and not just for Python) -- both instance and class variables are treated as the same.
enomado commentedon Apr 7, 2018
@tuukkamustonen looks like i've found the workaround for now.
This could be Generic with ClassT too, to act exact as
ClassInstanceVarshould.Probably we can also mix this with
Btw i'm not sure how to deal with set
A.f = 123error:Incompatible types in assignment (expression has type "int", variable has type "D[int]")
@gvanrossum, thanks!
[-]MyPy doesn't seem to distinguish between attributes of a class and attributes of its instances[/-][+]Distinguish between attributes of a class and attributes of its instances[/+]JukkaL commentedon Jan 29, 2020
Closing as a duplicate of #240.