As Python 3.8 releasing soon just wanted to know the difference between The final decorator and The Final annotation.
In Python 3.8 the Python type hinting feature (embodied by the typing module) will support marking names as final. This is documented in PEP 591 – Adding a final qualifier to typing.
What this means is that the typing module gains two new objects:
The typing.Final typing construct
The #typing.final() decorator.
To be up-front: The Python language itself does not gain final syntax or support. The above objects do not alter how Python works, they are constructs that merely document that an object or reference is to be considered final. The new objects can trivially be backported to older Python releases. The typing-extensions project, which provides backports of typing features from later to earlier Python versions, has already included these objects.
Their use lies in using a type hint checker like mypy to check that your project correctly treats the objects so documented as final.
Their use is otherwise exactly like that of the final keyword in Java: to state that a specific reference can only be assigned to once, that a method cannot be overridden in a subclass, or that a class definition cannot be subclassed.
Use the typing.Final object to mark a global or attribute as final, documenting that the value will never change once assigned to:
GLOBAL_CONSTANT: Final[str] = "This is a constant value because it is final"
Use the #typing.final decorator to mark a method as non-overridable (subclasses can't define a different implementation) or a class as non-inheritable (you can't create subclasses from the class):
#final
class FinalClass:
"""This class can't be inherited from"""
class SomeClass:
#final
def final_method(self):
"""Subclasses can't define a different final_method implementation"""
Also see the mypy documentation on their use, which covers such details as to what ways of assigning the value of a Final attribute are acceptable.
Demo:
$ cat demo.py
from typing import final, Final
# FOO is marked final, can't assign another value to it
FOO: Final[int] = 42
class Foo:
#final
def spam(self) -> int:
"""A final method can't be overridden in a subclass"""
return 42
#final
class Bar:
"""A final class can't be subclassed"""
# Rule breaking section
FOO = 81
class Spam(Foo, Bar):
def spam(self) -> int:
return 17
if __name__ == '__main__':
print("FOO:", FOO)
print("Spam().spam():", Spam().spam())
$ python3.8 demo.py # Python will not throw errors here
FOO: 81
Spam().spam(): 17
$ mypy demo.py # only a type checker will
demo.py:17: error: Cannot assign to final name "FOO"
demo.py:19: error: Cannot inherit from final class "Bar"
demo.py:20: error: Cannot override final attribute "spam" (previously declared in base class "Foo")
Related
I have been after a way to provide none initialized instance variables to my class. I found that we can actually do that using type hinting without assigning anything to them. Which does not seem to create it in anyway. For example:
class T:
def __init__(self):
self.a: str
def just_print(self):
print(self.a)
def assign(self):
self.a = "test"
Now lets say I run this code:
t = T()
t.just_print()
It will raise an AttributeError saying 'T' object has not attribute 'a'. Obviously, when I run this code, it prints test.
t = T()
t.assign()
t.just_print()
My question is, what happens behind the scene when I just do a: str? It doesn't get added to the class's attributes. But it doesn't cause any problem either. So... is it just ignored? This is python 3.8 by the way.
You're referring to type annotations, as defined by PEP 526:
my_var: int
Please note that type annotations differ from type hints, as defined by PEP 428:
def my_func(foo: str):
...
Type annotations have actual runtime effects. For example, the documentation states:
In addition, at the module or class level, if the item being annotated is a simple name, then it and the annotation will be stored in the __annotations__ attribute of that module or class [...]
So, by slightly modifying your example, we get this:
>>> class T:
... a: str
...
>>> T.__annotations__
{'a': <class 'str'>}
How can I cast a var into a CustomClass?
In Python, I can use float(var), int(var) and str(var) to cast a variable into primitive data types but I can't use CustomClass(var) to cast a variable into a CustomClass unless I have a constructor for that variable type.
Example with inheritance.
class CustomBase:
pass
class CustomClass(CustomBase):
def foo():
pass
def bar(var: CustomBase):
if isinstance(var, CustomClass):
# customClass = CustomClass(var) <-- Would like to cast here...
# customClass.foo() <-- to make it clear that I can call foo here.
In the process of writing this question I believe I've found a solution.
Python is using Duck-typing
Therefore it is not necessary to cast before calling a function.
Ie. the following is functionally fine.
def bar(var):
if isinstance(var, CustomClass):
customClass.foo()
I actually wanted static type casting on variables
I want this so that I can continue to get all the lovely benefits of the typing PEP in my IDE such as checking function input types, warnings for non-existant class methods, autocompleting methods, etc.
For this I believe re-typing (not sure if this is the correct term) is a suitable solution:
class CustomBase:
pass
class CustomClass(CustomBase):
def foo():
pass
def bar(var: CustomBase):
if isinstance(var, CustomClass):
customClass: CustomClass = var
customClass.foo() # Now my IDE doesn't report this method call as a warning.
In Python 4.0, type annotations like
foo:int
will be treated as a string and need to be dynamically resolved. This can be selected with python 3.7 using from __future import annotations.
The typing.get_type_hints function will try and turn string annotations and other forward references into actual types.
I need slightly different semantics than typing.get_type_hints because I'd like to handle class inheritance differently, although it looks from examination of the code like get_type_hints doesn't handle this case anyway.
Imagine the following code:
from __future__ import annotations
def foo():
class bar: pass
def baz(a: bar): pass
return baz
if I look at the annotations of the returned function I'll see something like
{'a': 'bar'}
Roughly what typing.get_type_hints does in this case is tries to find a locals and globals dictionary to pass into eval.
But how do I do that here? How do I find a set of locals where a will resolve to the bar class local to the call to foo that produced the baz I am examining?
I have this code:
from abc import ABCMeta, abstractmethod
class Instruction (object):
__metaclass__ = ABCMeta
def __init__(self, identifier_byte):
#type: (int) ->
self.identifier_byte = identifier_byte
#abstractmethod
def process (self):
print ("Identifier byte: ()".format(self.identifier_byte))
class LDAInstruction (Instruction):
def process (self):
super(Instruction,self).process()
with works fine with Python 3.2 but not with 2.6. Then based on this topic: TypeError: super() takes at least 1 argument (0 given) error is specific to any python version?
I changed the last line to:
super(Instruction,self).process()
which causes this error message on this precise line:
AttributeError: 'super' object has no attribute 'process'
For me it seems that there is a "process" method for the super invocation. Is Python saying that "super" is an independent object, unrelated to instruction? If yes, how can I tell it that super shall only invoke the base class constructor?
If not, how I shall proceed? Thanks for any ideas.
You're passing the wrong class to super in your call. You need to pass the class you're making the call from, not the base class. Change it to this and it should work:
super(LDAInstruction, self).process()
It's unrelated to your main error, but I'd further note that the base-class implementation of process probably has an error with its attempt at string formatting. You probably want {0} instead of () in the format string. In Python 2.7 and later, you could omit the 0, and just use {}, but for Python 2.6 you have to be explicit.
System:
- Windows 10 x64
- Python 3.6 x64
- PyCharm 2017.3.4
I am trying to better understand Python type annotation as relates to specifying class objects, rather than instances of classes. Suppose I have a super class and a sub class and I want to loop over a list of class objects and instantiate one of each.
In the code below I can get proper code-completion and tooltips in PyCharm when instantiating from the class directly or from a direct alias (using Ctrl-P for example). However, if I loop over the list and provide a type annotation (using typing.Type[Super1]) for the iterating variable cls it seems to annotate it as an instance, rather than as a class object. So I get proper code completion for the methods and properties, but then I don't get tooltips for the function arguments for the initialization.
How do I annotate it as a set of classes, rather than an instance of a set of classes (i.e. this variable is either the Super1 class or a sub-class of Super1). Is this the proper way but PyCharm just isn't recognizing it properly or is there actually a different way to do this.
import typing
class Super1:
def __init__(self, name: str):
self.name = name
self.meta = "super class"
def print_name(self):
print(self.name)
class Sub1(Super1):
def __init__(self, name: str):
super(Sub1, self).__init__(name)
self.meta = "sub class"
super_instance = Super1("name") # <-- get proper type hints in PyCharm
sub1_alias = Sub1
sub1_alias("name") # <-- get proper type hints for the __init__ function in PyCharm
cls: typing.Type[Super1]
for cls in (Super1, Sub1):
inst = cls("name") # <-- Ctrl-P does not give proper type hints for the __init__ function in PyCharm