-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Const type to indicate non-rebindable variables/attributes #1214
Comments
Early in the history of mypy I was actually planning to have something like this, but I guess I later forgot about it. This would likely require a PEP 484 (and potentially |
Const in Python could have similar guarantees to something like JavaScript ES6's const, to prevent reassignment https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const A clever programmer would be able to get around this through methods that mutate, or by a shared reference, but the guard rails are still nice. Alternatively, it could only be allowed for immutable non-container primitives such as int or str, or maybe even extended to tuples/namedtuples of immutable primitives as well |
We are now considering something like this, perhaps spelled as |
Fixes #1214 Fixes python/typing#286 Fixes python/typing#242 (partially, other part is out of scope) This is a working implementation of final access qualifier briefly discussed at PyCon typing meeting. Final names/attributes can be used to have more static guarantees about semantics of some code and can be used by other tools like mypyc for optimizations. We can play with this implementation before starting to write an actual PEP. The basic idea is simple: once declared as final, a name/attribute can't be re-assigned, overridden, or redefined in any other way. For example: ```python from typing import Final NO: Final = 0 YES: Final = 255 class BaseEngine: RATE: Final[float] = 3000 YES = 1 # Error! class Engine(BaseEngine): RATE = 9000 # Also an error! ``` For more use cases, examples, and specification, see the docs patch. Here are some comments on decisions made: * __What can be final?__ It is hard to say what semantic nodes are important, I started from just module and class constants, but quickly realized it is hard to draw the line without missing some use cases (in particular for mypyc). So I went ahead and implemented all of them, everything can be final: module constants, class-level and instance-level attributes, method, and also classes. * __Two names or one name?__ I currently use two names `Final` for assignments and `@final` for decorators. My PEP8-formatted mind just can't accept `@Final` :-) * __Should re-exported names keep they const-ness?__ I think yes, this is a very common pattern, so it looks like this is a sane default. * __What to do with instance-level vs class-level attributes?__ The point here is that mypy has a common namespace for class attributes. I didn't want to complicate things (including the mental model), so I just decided that one can't have, e.g., a name that is constant on class but assignable on instances, etc. Such use cases are relatively rare, and we can implement this later if there will be high demand for this. ...deferred features: * I didn't implement any constant propagation in mypy _yet_. This can be done later on per use-case basis. For example: ```python fields: Final = [('x', int), ('y', int)] NT = NamedTuple('NT', fields) ``` * __Should final classes be like sealed in Scala?__ I think probably no. On one hand it could be be a nice feature, on other hand it complicates the mental model and is less useful for things like mypyc. * I don't allow `Final` in function argument types. One argument is simplicity, another is I didn't see many bugs related to shadowing an argument in function bodies, finally people might have quite different expectations for this. If people will ask, this would be easy to implement. ...and implementation internals: * There are two additional safety nets that I don't mention in the docs: (a) there can be no `TypeVar`s in the type of class-level constant, (b) instance-level constant can't be accessed on the class object. * I generate errors for re-definitions in all subclasses, not only in immediate children. I think this is what most people would want: turning something into a constant will flag most re-assignment points. * We store the `final_value` for constants initialized with a simple literal, but we never use it. This exists only for tools like mypyc that may use it for optimizations. cc @ambv @rchen152 @vlasovskikh
As a Python developer, I would like a Const type so that I can indicate that a variable or attribute may not be rebound to another value. This would be especially useful for module and class constants, but could also be helpful for instance attributes that really shouldn't be modified. Not only does it help prevent accidentally overwriting constants, it would hopefully reduce the need to use
@property
on non-critical attributes. Here is a contrived example:kind = MANAGER # type: Const
reqiures the Const type to rebind kind without an error just as a const variable in C requires a cast during reassignment to quiet compiler warnings/errors.I'm not sure if it makes sense to allow Const to take parameters as the type can be inferred in most cases. One might want other variables, such as those passed into functions, to be const, in which case it makes sense to allow Const to take parameters. Here is an example:
The text was updated successfully, but these errors were encountered: