-
Notifications
You must be signed in to change notification settings - Fork 250
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
Final classes and member functions #286
Comments
It's an interesting idea. In Python's past things like this have been
proposed many times, but usually with the idea that a metaclass would have
to be added that enforces it (like with `@abstractmethod`). I wonder if
(like that) this should live in the `abc` module, possibly with an
implementation that enforces it. But the type checker could still enforce
it too. Alternatively it could live in typing and be purely a type checker
feature.
OTOH Wouldn't a comment (plus a note in any docs for the class) be just as
effective? (Not, I guess, if you have an existing class or method that you
want to change to be final -- the `@final` decorator would allow you to
find any violations quicker than a grep, perhaps, and catch future
violations.)
|
yeah, I really want this for the enforcement use case. I suppose failure On Wed, Sep 28, 2016 at 8:22 AM Guido van Rossum [email protected]
|
Given the (relative) uniqueness of your use case you're probably better off
hacking your own metaclass. But I believe I've said that before. :-)
|
You have, but at the same time you suggested I come here to too On Wed, Sep 28, 2016, 3:37 PM Guido van Rossum [email protected]
|
Yeah, so we can gauge interest.
|
At the moment I'm thinking it's probably easier to write a linter for my On Wed, Sep 28, 2016, 7:34 PM Guido van Rossum [email protected]
|
|
@elazarg I agree that the semantics you described would be nice. |
I have released a utility library that can make classes |
The purpose of the sealed/final keywords is the same as making something in C++ nonvirtual: It allows you to improve the performance of a bottlenecked bit of code. As I understand it, method lookup in Python is always...well, a lookup. No performance benefit. So this run-time check is a rather weird, un-Pythonic hack. Rather than creating a run-time failure to enforce something, I'd recommend providing the consumers of your class with some clear tests that give examples of how your code is to be properly used. If this is a team you have influence over, then perhaps provide some guidance via lunch-n-learn sessions, pair-programming, or mob-programming on basic OO principles, S.O.L.I.D., patterns wisdom (e.g., "Favor delegation over inheritance" --GoF), and Beck's Four Rules of Simple Design (e.g., "Testability trumps everything else, because everything else is made malleable through tests." -- me, just now). |
Final functions and classes allow you to reason better about the behavior of the program, making your program easier to understand. Being able to e.g. inline function calls is just one consequence. This doesn't mean of course that runtime failures is the right way to implement such feature. |
@AgileInstitute Please stop lecturing. |
My apologies. Got a little off-track. |
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
@dgoldstein0 I've been working on a base class (not a metaclass) that does various related checks. I've considered a final method decorator (https://github.com/NeilGirdhar/ipromise). If this looks interesting to you, please feel free to add a final decorator and check in the base class. |
In some languages (e.g. java) it's possible to declare a class as final so that it can't be subclassed; similarly, it's possible to declare functions as
final
so that they can't be overridden by sublclasses. It would be great if typing could support this - so that classes could be annotated as final or their functions as final, and then tools like mypy could enforce it.E.g.
(reposting from python/mypy#2032)
The text was updated successfully, but these errors were encountered: