Typehints for python class - python

Is it possible to typehint a class's self?
The reason being is basing a class off of an ambiguously dynamic class that has definitions given in hint stubs called BaseClassB and SubClassD.
I would have expected this to be valid python, but it's not. Is there a way to typehint the baseclass argument(s) to creating a class?
I'd also accept any tricks that get PyCharm to autocomplete off self correctly as an answer as this doesn't seem to be supported Python in 3.7.4.
e.g.
class MyClass(BaseClassAmbiguous: Union[BaseClassB, SubClassD])
def func(self):
self.self_doesnt_autocomplete_correctly
self. # Desired functionality is this to autocomplete according to BaseClassB and SubClassD

I suspect the reason why your type checker is choking on your code is because it's not actually valid syntax. The annotation in your base class list is a syntax error.
Probably the best available workaround is to just give BaseClassAmbiguous a fake type, like so:
from typing import Union, TYPE_CHECKING
if TYPE_CHECKING:
class BaseClassAmbiguous(BaseClassB, SubClassD): pass
else:
# Create your ambiguous base class however it's actually
# constructed at runtime
class MyClass(BaseClassAmbiguous):
def func(self) -> None:
self.blah
Basically, lie to your type-checker and pretend that BaseClassAmbiguous directly inherits from your two classes. I'm not sure if Pycharm specifically supports this kind of thing, but it's something it in principle ought to support. (E.g. it's possible to do these kinds of shenanigans in Python).
That said, if you're going to use this approach, you're probably better off just having BaseClassAmbiguous actually inherit directly from both subclasses if at all possible.
To answer your original question, yes, it's legal to annotate the self method. That technique is usually reserved for when you want to have your self variable be generic -- see this example in PEP 484, and this mini-tutorial in the mypy docs.
But you could in principle annotate self using any type hint, really, including Unions, as long as it's not fundamentally incompatible with what your class really is -- your annotation for self would need to essentially be the same type as or a supertype of MyClass.
That said, this technique will likely not help you here: what you'd need is an intersection type, not a union type, and annotating self won't help resolve the syntax error in your base class list.
I think the more broad problem here is that the PEP 484 ecosystem doesn't necessarily deal well with ambiguous or dynamic base classes. Perhaps this is not possible to do in your case, but If I were in your shoes, I'd concentrate my efforts on making the base class more concrete.

Related

Python - how to make abstract class constants?

What is the python way of defining abstract class constants?
For example, if I have this abstract class:
class MyBaseClass(SomeOtherClass, metaclass=ABCMeta):
CONS_A: str
CONS_B: str
So, the type checker/"compiler" (at least Pycharm's one) doesn't complain about the above.
But if I inherit it to another class and don't implement CONS_A and CONS_B, it doesn't complain either:
class MyChildClass(MyBaseClass):
pass
What is the python way of enforcing (the much we can with a duck-type language) implementation of MyBaseClass to actually implement CONS_A and CONS_B?
Bonus question: once I can enforce, how can I only enforce for non-abstract classes? I suppose it should be the same answer as the main question. But, I might be wrong. So, for example:
# "compiler" should not complain
class MyChildAbstractClass(MyBaseClass):
pass
# here it must complain if I don't implement `CONS_A` and `CONS_B`
class MyChildImplementationClass(MyChildAbstractClass):
pass
Obs: I am aware of #abstractmethod, but I didn't find a solution for abstract class constants.
Thank you.

Programming Languages where a keyword is needed to specify that the method is extended from its parent class

Forgive me for my ignorance, but does anyone know any languages that strictly enforce the condition I've given on the title? For example, using Python syntax, we can extend a class with a new method like this
class A:
pass
class B(A):
def foo(self):
pass
But is there a language that needs an additional keyword, let's say new, to specify that this method is unique to the child class and is not an override of the methods of its parent class/es? For example:
class A:
pass
class B(A):
def new foo(self):
pass
I am asking this because, when I am working on a project that requires multiple inheritance such as class B(A, C, D), and I saw a method defined in B, I need to check if the given method is from one of its parent class or its own method, and I find it extremely tedious.
The closest I can think of is the #Override annotation in Java, which can be applied to a method declaration in order for the compiler to check that it overrides an inherited method (or implements an interface method).
When used in conjunction with a linter which checks that all method overrides are annotated with #Override, then your IDE will give you a linter warning when you omit the annotation. IntellIJ IDEA and SonarSource both have linter rules for this, for example.
So long as you are strict about obeying the linter warning, then it's "strict" in that sense, but of course linter warnings don't actually prevent your code from being compiled or executed. Nonetheless, I don't know of a closer example from a real programming language. Unfortunately Java doesn't have multiple inheritance so it's not directly applicable to your problem.

Type hinting propagation for overriding methods

Is it possible to propagate type hinting to overridding methods?
Say, I have the following classes:
class Student:
def study():
pass
class Option:
self.option_value
class BaseChoice:
def make_choice(self, student, options):
"""
:type student: Student
:type options: list[Option]
"""
class RationalChoice(BaseChoice):
def make_choice(self, student, options):
pass
When I'm inside RationalChoice.make_choice pycharm does not suggest autocomplete for options properties/methods, however it picks right hint for student. The obvious workaround would be to just copy the docstring, but I'm going to have tens of different BaseChoice descendants, so it's just not practical.
I'm using PyCharm 3.1.1, both Community and Professional versions are affected.
Is it something completely missing in python itself, or just in PyCharm?
PyCharm does not look at the superclass type hints when overriding methods. I don't know if this is a bug or a feature, although I would be leaning toward the latter: Python does not require overriding methods to have the same signatures or accept the same types as the methods they override. Put differently, type hints on BaseChoice are not automatically valid for RationalChoice.
What PyCharm does do, and what seems to be confusing you, is take a quick guess and decide that Student would be a sensible class for a parameter called student. There is no class Options, so that heuristic fails.
So if you really, really want type hints, there's really no alternative to specifying them everywhere you want them.
If you are using Python 3, you could try the new in-language type hint (annotation) syntax:
class RationalChoice(BaseChoice):
def make_choice(self, student: Student, options: list):
return

Python abstract classes - how to discourage instantiation?

I come from a C# background where the language has some built in "protect the developer" features. I understand that Python takes the "we're all adults here" approach and puts responsibility on the developer to code thoughtfully and carefully.
That said, Python suggests conventions like a leading underscore for private instance variables. My question is, is there a particular convention for marking a class as abstract other than just specifying it in the docstrings? I haven't seen anything in particular in the python style guide that mentions naming conventions for abstract classes.
I can think of 3 options so far but I'm not sure if they're good ideas:
Specify it in the docstring above the class (might be overlooked)
Use a leading underscore in the class name (not sure if this is universally understood)
Create a def __init__(self): method on the abstract class that raises an error (not sure if this negatively impacts inheritance, like if you want to call a base constructor)
Is one of these a good option or is there a better one? I just want to make sure that other developers know that it is abstract and so if they try to instantiate it they should accept responsibility for any strange behavior.
If you're using Python 2.6 or higher, you can use the Abstract Base Class module from the standard library if you want to enforce abstractness. Here's an example:
from abc import ABCMeta, abstractmethod
class SomeAbstractClass(object):
__metaclass__ = ABCMeta
#abstractmethod
def this_method_must_be_overridden(self):
return "But it can have an implementation (callable via super)."
class ConcreteSubclass(SomeAbstractClass):
def this_method_must_be_overridden(self):
s = super(ConcreteSubclass, self).this_method_must_be_overridden()
return s.replace("can", "does").replace(" (callable via super)", "")
Output:
>>> a = SomeAbstractClass()
Traceback (most recent call last):
File "<pyshell#13>", line 1, in <module>
a = SomeAbstractClass()
TypeError: Can't instantiate abstract class SomeAbstractClass with abstract
methods this_method_must_be_overridden
>>> c = ConcreteSubclass()
>>> c.this_method_must_be_overridden()
'But it does have an implementation.'
Based on your last sentence, I would answer answer "just document it". Anyone who uses a class in a way that the documentation says not to must accept responsibility for any strange behavior.
There is an abstract base class mechanism in Python, but I don't see any reason to use it if your only goal is to discourage instantiation.
I just name my abstract classes with the prefix 'Abstract'. E.g. AbstractDevice, AbstractPacket, etc.
It's about as easy and to the point as it comes. If others choose to go ahead and instantiate and/or use a class that starts with the word 'Abstract', then they either know what they're doing or there was no hope for them anyway.
Naming it thus, also serves as a reminder to myself not to go nuts with deep abstraction hierarchies, because putting 'Abstract' on the front of a whole lot of classes feels stupid too.
Create your 'abstract' class and raise NotImplementedError() in the abstract methods.
It won't stop people using the class and, in true duck-typing fashion, it will let you know if you neglect to implement the abstract method.
In Python 3.x, your class can inherit from abc.ABC.
This will make your class non-instantiable and your IDE will warn you if you try to do so.
import abc
class SomeAbstractClass(abc.ABC):
#abc.abstractmethod
def some_abstract_method(self):
raise NotImplementedError
#property
#abc.abstractmethod
def some_abstract_property(self):
raise NotImplementedError
This has first been suggested in PEP 3119.
To enforce things is possible, but rather unpythonic. When I came to Python after many years of C++ programming I also tried to do the same, I suppose, most of people try doing so if they have an experience in more classical languages. Metaclasses would do the job, but anyway Python checks very few things at compilation time. Your check will still be performed at runtime. So, is the inability to create a certain class really that useful if discovered only at runtime? In C++ (and in C# as well) you can not even compile you code creating an abstract class, and that is the whole point -- to discover the problem as early as possible. If you have abstract methods, raising a NotImplementedError exception seems to be quite enough. NB: raising, not returning an error code! In Python errors usually should not be silent unless thay are silented explicitly. Documenting. Naming a class in a way that says it's abstract. That's all.
Quality of Python code is ensured mostly with methods that are quite different from those used in languages with advanced compile-time type checking. Personally I consider that the most serious difference between dynamically typed lngauges and the others. Unit tests, coverage analysis etc. As a result, the design of code is quite different: everything is done not to enforce things, but to make testing them as easy as possible.

Python how to get the base instance of an instance?

In C# I would go:
myObj.base
I have a Date class which inherits from date.datetime. The Date class overrides __gt__() and __lt__() so when using the < and > operators they are called. I do not want to use these overrides - I want to use the date.datetime methods on an instance of Date.
Use super() to get the superclass object. Type help(super) in the Python command prompt.
From the manual:
class super(object)
| super(type) -> unbound super object
| super(type, obj) -> bound super object; requires isinstance(obj, type)
| super(type, type2) -> bound super object; requires issubclass(type2, type)
| Typical use to call a cooperative superclass method:
| class C(B):
| def meth(self, arg):
| super(C, self).meth(arg)
If I understand your question correctly, this doesn't make sense in Python. There's no "base instance" inside the instance of a subclass.
A Python instance is just one thing, containing a collection of attributes (any of which may have been set/modified by any of its base classes, or indeed from code outside any of its classes at all). Indeed it's possible to change the class of an instance at runtime, even transplanting it into an entirely different inheritance heirarchy (this is not frequently a good idea, but it's well-defined). No matter what, the instance remains a single unitary object, which only knows what attributes it has and which class it's an instance of (and in fact that's just an attribute: __class__).
Edit: If what you want is to be able to invoke overridden methods on an instance, then you do that by using super, as hocl answered. However it seems from your comments that you do not fully grok what super is doing (which is natural, as it's quite complex).
super(Date, myObj) doesn't return "the underlying datetime.date" instance, because there's no such thing, only the myObj object. Although for your purposes it sounds like this will fill your needs (and you can probably stop at this sentence).
What it does is return is a magical wrapper around myObj that looks up methods starting just "behind" Date; i.e. it finds the method that would be called if Date didn't override it. So in this case it will find all methods from datetime.date, because you only have single inheritance going on.
A key difference is that this supports multiple inheritance. If someone makes another class that inherits from Date and also inherits from datetime.date by another path, then super(Date, instanceOfThatClass) may not actually hit datetime.date's methods. It depends on the details of that inheritance heirarchy. This is actually the situation super was designed for though; it enabled classes in complex multiple inheritance hierarchies to cooperatively call each other's implementations, ensuring that each is called only once, and in an order that is sensible (though it may not be the same order for each ultimate leaf class, so classes in the middle of the hierarchy actually don't know which super-class implementation they're calling). My understanding is this complex situation cannot arise in C#, hence it can provide a simple .base syntax.
My understanding is also (and this is a bit of a guess), that because C# is statically typed and supports inheriting from classes defined in pre-compiled libraries, that when you have class Sub inheriting from class Base, inside an instance of Sub there really is a complete instance of Base which you can get at and then call methods on. This will affect shadowed fields; I would expect (again, as a non-C# programmer guessing a bit) that after getting the Base instance from a Sub instance, any direct reference to a field overridden by Sub would hit the field from Base, not the one from Sub.
In Python, OTOH, there is no base instance, and classes can't override fields. If the constructor and methods of Date and datetime.date both refer to the same field, it's just the one field in the instance that they're both sharing. So using super won't change what field you'll access, as you might expect if you think of it as getting the base instance.
Given that you're not using a complex multiple inheritance situation, if you wanted a simple syntax you could actually call Date.__lt__(myObj, otherObj) directly, though it looks ugly because you don't get to use the infix operator syntax when you do it that way. It's less horrible if you're considering ordinary methods; in that case it's possibly simpler than using super.
Take home message: I'm pretty sure super(Date, myObj) is what you want in this case. But if you get into more complicated situations, you don't want to think of super as the way to get "the base instance", like you would in C#. That understanding will trip you up when in multiple inheritance (which can be bloody confusing anyway), but also when you have multiple layers in the inheritance hierarchy using the same field.

Categories