<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dag Brattli</title>
    <description>The latest articles on DEV Community by Dag Brattli (@dbrattli).</description>
    <link>https://dev.to/dbrattli</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F286813%2F6321867c-34f9-4f91-8496-35653e743da7.jpg</url>
      <title>DEV Community: Dag Brattli</title>
      <link>https://dev.to/dbrattli</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dbrattli"/>
    <language>en</language>
    <item>
      <title>Python Type Annotations (part 3)</title>
      <dc:creator>Dag Brattli</dc:creator>
      <pubDate>Sun, 10 Aug 2025 05:59:28 +0000</pubDate>
      <link>https://dev.to/cardamomcode/python-type-annotations-part-3-ihf</link>
      <guid>https://dev.to/cardamomcode/python-type-annotations-part-3-ihf</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Python Type Annotations&lt;/strong&gt; is a tutorial in 3 parts: &lt;a href="https://cardamomcode.dev/python-type-annotations-part-1" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt; | &lt;a href="https://cardamomcode.dev/python-type-annotations-part-2" rel="noopener noreferrer"&gt;Part 2&lt;/a&gt; | Part 3 (this post)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Variance in Generics&lt;/li&gt;
&lt;li&gt;Covariance&lt;/li&gt;
&lt;li&gt;Contravariance&lt;/li&gt;
&lt;li&gt;Invariance&lt;/li&gt;
&lt;li&gt;References&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Variance in Generics
&lt;/h2&gt;

&lt;p&gt;Variance in generics refers to how subtyping relationships behave when they are wrapped in a generic container or function. E.g. if &lt;code&gt;Cat&lt;/code&gt; is a subclass of &lt;code&gt;Animal&lt;/code&gt;, then subtype polymorphism which you may already be familiar with, explains how a &lt;code&gt;Cat&lt;/code&gt; can transform (morph) into, and be used in place of an &lt;code&gt;Animal&lt;/code&gt;. In a similar way, variance tells us how e.g. a &lt;code&gt;set[Cat]&lt;/code&gt; can transform (vary) and be used in place of a &lt;code&gt;set[Animal]&lt;/code&gt; or vice versa.&lt;/p&gt;

&lt;p&gt;There are three types of variance:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Covariance&lt;/strong&gt; enables you to use a more specific type than originally specified. Example: If &lt;code&gt;Cat&lt;/code&gt; is a subclass of &lt;code&gt;Animal&lt;/code&gt;, you can assign an instance of &lt;code&gt;Iterable[Cat]&lt;/code&gt; to a variable of type &lt;code&gt;Iterable[Animal]&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Contravariance&lt;/strong&gt; enables you to use a more general type than originally specified. Example: If &lt;code&gt;Cat&lt;/code&gt; is a subclass of &lt;code&gt;Animal&lt;/code&gt;, you can assign an instance of &lt;code&gt;Callable[[Animal], None]&lt;/code&gt; to a variable of type &lt;code&gt;Callable[[Cat], None]&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Invariance&lt;/strong&gt; means that you can only use the type originally specified. An invariant generic type parameter is neither covariant nor contravariant. Example: If &lt;code&gt;Cat&lt;/code&gt; is a subclass of &lt;code&gt;Animal&lt;/code&gt;, you cannot assign an instance of &lt;code&gt;list[Animal]&lt;/code&gt; to a variable of type &lt;code&gt;list[Cat]&lt;/code&gt; or vice versa.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If we look at Python types, there are already many types and combinations of types that have different variance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Function types i.e. callables are covariant on their return type, and contravariant on their arguments.&lt;/li&gt;
&lt;li&gt;Mutable containers like &lt;code&gt;list&lt;/code&gt; and &lt;code&gt;dict&lt;/code&gt; are invariant.&lt;/li&gt;
&lt;li&gt;Immutable containers like &lt;code&gt;tuple&lt;/code&gt; and &lt;code&gt;set&lt;/code&gt; are covariant.&lt;/li&gt;
&lt;li&gt;Union types are covariant. This means that optional types are also covariant because they are equivalent to &lt;code&gt;T | None&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding variance helps you writing more flexible and type-safe code, especially when working with container types, generics and inheritance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Covariance
&lt;/h2&gt;

&lt;p&gt;Covariance (co- = together) means the subtype relationship goes in the same direction i.e. transform (vary) together with the wrapped type. For example, if &lt;code&gt;Cat&lt;/code&gt; is a subtype of &lt;code&gt;Animal&lt;/code&gt;, then &lt;code&gt;set[Cat]&lt;/code&gt; is a subtype of &lt;code&gt;set[Animal]&lt;/code&gt;. The type parameter varies together and in the same direction as the inheritance relationship.&lt;/p&gt;

&lt;p&gt;As we mentioned in the introduction, covariance is a type of variance that allows you to use a more specific type than originally specified. For example, if &lt;code&gt;Cat&lt;/code&gt; is a subclass of &lt;code&gt;Animal&lt;/code&gt;, you can assign an instance of &lt;code&gt;Iterable[Cat]&lt;/code&gt; to a variable of type &lt;code&gt;Iterable[Animal]&lt;/code&gt;, and if you have a method that takes an &lt;code&gt;Iterable[Animal]&lt;/code&gt;, you can safely pass in an &lt;code&gt;Iterable[Cat]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The definition is that a generic type &lt;code&gt;GenericType[T]&lt;/code&gt; is covariant in type parameter &lt;code&gt;T&lt;/code&gt; if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Derived&lt;/code&gt; is subtype of &lt;code&gt;Base&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GenericType[Derived]&lt;/code&gt; is a subtype of &lt;code&gt;GenericType[Base]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples of covariant types in Python are &lt;code&gt;Iterable&lt;/code&gt;, &lt;code&gt;set&lt;/code&gt;, &lt;code&gt;tuple&lt;/code&gt;, and return types of callables.&lt;/p&gt;

&lt;p&gt;Before we start we need to import a few types that we will use in the examples.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;abc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;abstractmethod&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections.abc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Iterable&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Generic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TypeVar&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's define a few classes. We will use &lt;code&gt;Animal&lt;/code&gt; as a base class, and define &lt;code&gt;Cat&lt;/code&gt; and &lt;code&gt;Dog&lt;/code&gt; as subclasses of &lt;code&gt;Animal&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Meow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Woof&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's first see how this works with basic assignments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Iterable&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cats&lt;/span&gt;  &lt;span class="c1"&gt;# Ok
&lt;/span&gt;&lt;span class="n"&gt;ys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cats&lt;/span&gt;  &lt;span class="c1"&gt;# Error: ...
# Type parameter "_T@list" is invariant, but "Cat" is not the same as "Animal"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we see for the first assignment everything is ok, since the type of &lt;code&gt;xs&lt;/code&gt; is &lt;code&gt;Iterable[Animal]&lt;/code&gt;, which is indeed a subtype of &lt;code&gt;Iterable[Cat]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But for the second assignment, we get an error. This is because &lt;code&gt;list[Animal]&lt;/code&gt; is invariant, and &lt;code&gt;list[Cat]&lt;/code&gt; is not a subtype of &lt;code&gt;list[Animal]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The problem is that lists are mutable. Think for a minute what would happen if we appended a &lt;code&gt;Dog&lt;/code&gt; to the list &lt;code&gt;ys&lt;/code&gt;. This is fine for &lt;code&gt;ys&lt;/code&gt; since the type is &lt;code&gt;list[Animal]&lt;/code&gt;, but this means that &lt;code&gt;cats&lt;/code&gt; would also be modified and would now contain a &lt;code&gt;Dog&lt;/code&gt;, which is not allowed and should come as a surprise to any code still using &lt;code&gt;cats&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cats&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;span class="n"&gt;ys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cats&lt;/span&gt;  &lt;span class="c1"&gt;# type: ignore
&lt;/span&gt;&lt;span class="n"&gt;ys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;say&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cat&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cats&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="c1"&gt;# Output: ['Meow', 'Meow', 'Woof']
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Covariance And Function Return Types
&lt;/h3&gt;

&lt;p&gt;Now let's see how this works with function return types for callables. We will define some "getter" functions that returns an instance of &lt;code&gt;Animal&lt;/code&gt; or &lt;code&gt;Cat&lt;/code&gt;, and also a "setter" function just to show that this does not work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_animal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Cat, but returned as Animal
&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_animals&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Iterable&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nc"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;()])&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_cat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_cat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_cats&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Iterable&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nc"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;()])&lt;/span&gt;


&lt;span class="c1"&gt;# Cat -&amp;gt; Animal
&lt;/span&gt;&lt;span class="n"&gt;var1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Animal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Ok, polymorphism
&lt;/span&gt;&lt;span class="n"&gt;var2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="p"&gt;[[],&lt;/span&gt; &lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_cat&lt;/span&gt;  &lt;span class="c1"&gt;# Ok, covariance,
&lt;/span&gt;&lt;span class="n"&gt;var3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="p"&gt;[[],&lt;/span&gt; &lt;span class="n"&gt;Iterable&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_cats&lt;/span&gt;  &lt;span class="c1"&gt;# Ok, covariance
&lt;/span&gt;&lt;span class="n"&gt;var4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;set_cat&lt;/span&gt;  &lt;span class="c1"&gt;# Error: ...
# Parameter 1: type "Animal" is incompatible with type "Cat"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first assignment of &lt;code&gt;var1&lt;/code&gt; is just normal polymorphism. This is just to show the similarity between polymorphism and covariance. For the second and third assignments we see that covariance works for return types in callables since a function that returns a &lt;code&gt;Cat&lt;/code&gt; is compatible with a function that returns an &lt;code&gt;Animal&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For the last assignment, we get an error, since &lt;code&gt;set_cat&lt;/code&gt; is a function that takes a &lt;code&gt;Cat&lt;/code&gt;, and &lt;code&gt;set_cat&lt;/code&gt; is not compatible with a function that takes an &lt;code&gt;Animal&lt;/code&gt;. This is because callables are not covariant on parameter types.&lt;/p&gt;

&lt;p&gt;In the next example, we will see how this works when assigning a general type e.g. &lt;code&gt;Animal&lt;/code&gt; to a more specific type e.g &lt;code&gt;Cat&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Animal -&amp;gt; Cat
&lt;/span&gt;&lt;span class="n"&gt;var5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Cat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Error: "Animal" is incompatible with "Cat"
&lt;/span&gt;&lt;span class="n"&gt;var6&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="p"&gt;[[],&lt;/span&gt; &lt;span class="n"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_animal&lt;/span&gt;  &lt;span class="c1"&gt;# Error: ...
&lt;/span&gt;&lt;span class="n"&gt;var7&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="p"&gt;[[],&lt;/span&gt; &lt;span class="n"&gt;Iterable&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_animals&lt;/span&gt;  &lt;span class="c1"&gt;# Error: ...
# Type parameter "_T_co@Iterable" is covariant, but "Animal" is not a subtype of "Cat"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the first assignment of &lt;code&gt;var5&lt;/code&gt;, we get an error, since &lt;code&gt;Animal&lt;/code&gt; is not a subtype of &lt;code&gt;Cat&lt;/code&gt;. This is because a function that returns an &lt;code&gt;Animal&lt;/code&gt; is not compatible with a function that returns a &lt;code&gt;Cat&lt;/code&gt;. This is because the function might return a Dog.&lt;/p&gt;

&lt;p&gt;For the second assignment of &lt;code&gt;var6&lt;/code&gt;, and third assignments of &lt;code&gt;var7&lt;/code&gt;, we also get errors, since &lt;code&gt;Animal&lt;/code&gt; is not a subtype of &lt;code&gt;Cat&lt;/code&gt;, hence a &lt;code&gt;Dog&lt;/code&gt; might be returned from &lt;code&gt;get_animal&lt;/code&gt; or &lt;code&gt;get_animals&lt;/code&gt; which is incompatible with &lt;code&gt;Cat&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Covariant Generic Classes
&lt;/h3&gt;

&lt;p&gt;We can also define our own covariant generic classes. Let's see how this works. To define a covariant generic class, we need to use the &lt;code&gt;covariant&lt;/code&gt; keyword argument for the &lt;code&gt;T_co&lt;/code&gt; type variable.` This is by the way similar to how we declared type variables before Python 3.12.&lt;/p&gt;

&lt;p&gt;We will make a &lt;code&gt;Rabbit&lt;/code&gt; class that is a subclass of &lt;code&gt;Animal&lt;/code&gt;, and a &lt;code&gt;Hat&lt;/code&gt; class that is covariant in its type parameter. This means that a &lt;code&gt;Hat[Rabbit]&lt;/code&gt; is a subtype of &lt;code&gt;Hat[Animal]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1738417766106%2F260c442e-424c-4d34-b2f5-03443ab96996.png%2520align%3D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1738417766106%2F260c442e-424c-4d34-b2f5-03443ab96996.png%2520align%3D" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's see how this looks in code.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`python&lt;br&gt;
T_co = TypeVar("T_co", covariant=True)&lt;/p&gt;

&lt;p&gt;class Rabbit(Animal):&lt;br&gt;
    def say(self) -&amp;gt; str:&lt;br&gt;
        return "Squeak"&lt;/p&gt;

&lt;p&gt;class Hat(Generic[T_co]):&lt;br&gt;
    def &lt;strong&gt;init&lt;/strong&gt;(self, value: T_co) -&amp;gt; None:&lt;br&gt;
        self.value = value&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def pull(self) -&amp;gt; T_co:
    return self.value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;One way to think about covariance is that covariant types are "out" types and can only be used as return types. If a class were to allow inserting and setting values of the generic type, it would violate the principle of covariance and could lead to type safety issues.&lt;/p&gt;

&lt;p&gt;Let's see what happens if we try to add a method that takes the generic type as input for a class with the covariant type variable.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`python&lt;br&gt;
class InvalidHat(Hat[T_co]):&lt;br&gt;
    """Just to show that in parameters are not allowed"""&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def put(self, value: T_co) -&amp;gt; None:  # Error: Covariant type variable cannot be used in parameter type
    self.value = value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;As we see in the example above, we get an error when we try to add a method that takes the generic type in the parameter. This is because covariant types can only be used as return types. If a class were to allow inserting and setting values of the generic type, it could lead to type safety issues.&lt;/p&gt;

&lt;p&gt;But wait a minute. The constructor or the initializer &lt;code&gt;__init__&lt;/code&gt; method is taking the generic type as an argument. Why isn't that a problem?  The reason why this is okay is that the object is being created at that point, and the type is being established. Once the object is created, its type is fixed and won't change.&lt;/p&gt;

&lt;p&gt;But for abstract covariant classes or protocols that do not have constructors, we can only use the generic type as on out type, i.e. only in the return type of a method.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`python&lt;br&gt;
hat_with_animal: Hat[Animal] = Hat&lt;a href="https://dev.toRabbit()"&gt;Rabbit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;def fetch_rabbit_hat() -&amp;gt; Hat[Rabbit]:&lt;br&gt;
    return Hat(Rabbit())&lt;/p&gt;

&lt;p&gt;fetch_animal_hat: Callable[[], Hat[Animal]] = fetch_rabbit_hat&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In the example above we defined &lt;code&gt;Hat[Rabbit]&lt;/code&gt; and assign it to a variable that has the type &lt;code&gt;Hat[Animal]&lt;/code&gt;. This would have given an error if the generic type &lt;code&gt;T_co&lt;/code&gt; was not covariant.&lt;/p&gt;

&lt;p&gt;Note: we specify &lt;code&gt;Hat[Rabbit](Rabbit())&lt;/code&gt; to avoid that the constructor uses polymorphism from &lt;code&gt;Rabbit&lt;/code&gt; to &lt;code&gt;Animal&lt;/code&gt; so we do create a &lt;code&gt;Hat[Rabbit]&lt;/code&gt; and not a &lt;code&gt;Hat[Animal]&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Covariance summarized
&lt;/h3&gt;

&lt;p&gt;Covariance is in may ways similar to polymorphism in the way we think about and use the type in our code. We use it for "out" types we have in our methods, i.e. methods that returns the generic type like &lt;code&gt;Iterable&lt;/code&gt;, &lt;code&gt;Set&lt;/code&gt;, &lt;code&gt;Tuple&lt;/code&gt;, and also return types of &lt;code&gt;Callable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When we define a type to be covariant, we are able to assign a container i.e. generic class of e.g. &lt;code&gt;Rabbit&lt;/code&gt;, to a variable that is annotated as a generic class of &lt;code&gt;Animal&lt;/code&gt;. This would not have been possible if the type was not covariant.&lt;/p&gt;
&lt;h1&gt;
  
  
  Contravariance
&lt;/h1&gt;

&lt;p&gt;Contravariance (contra- = against/opposite) means the subtype relationship goes in the opposite direction. If &lt;code&gt;Cat&lt;/code&gt; is a subtype of &lt;code&gt;Animal&lt;/code&gt;, then e.g &lt;code&gt;Observer[Animal]&lt;/code&gt; is a subtype of &lt;code&gt;Observer[Cat]&lt;/code&gt;. The type parameter varies in the opposite direction from the inheritance relationship of the wrapped type.&lt;/p&gt;

&lt;p&gt;As mentioned introduction, contravariance is a type of variance that allows you to use a more general type in place of a more specific type. This  might sound counterintuitive at first. It usually goes against what you would expect, and it's safe to say that this is something most developers don't know about.&lt;/p&gt;

&lt;p&gt;In Python, contravariance is typically experienced for function arguments in callables, or push-based containers such as observables (&lt;a href="https://github.com/ReactiveX/RxPY" rel="noopener noreferrer"&gt;RxPY&lt;/a&gt;). This means that it might help to think in terms of callbacks when you try to understand contravariance. This is because callbacks are usually functions that usually takes one or more arguments and returns nothing.&lt;/p&gt;

&lt;p&gt;The definition is that a generic type &lt;code&gt;GenericType[T]&lt;/code&gt; is contravariant in type parameter &lt;code&gt;T&lt;/code&gt; if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Derived&lt;/code&gt; is subtype of &lt;code&gt;Base&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GenericType[Base]&lt;/code&gt; is a subtype of &lt;code&gt;GenericType[Derived]&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples of contravariant types in Python are callables, and function arguments. The Observer class in RxPY and similar classes using the "consumer"" pattern e.g (&lt;code&gt;send&lt;/code&gt;, &lt;code&gt;throw&lt;/code&gt;, &lt;code&gt;close&lt;/code&gt;) style of methods that take generic type &lt;code&gt;T&lt;/code&gt; as an argument and return nothing.&lt;/p&gt;

&lt;p&gt;First, we need to import a few types that we will use in the examples.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;python&lt;br&gt;
from abc import abstractmethod&lt;br&gt;
from collections.abc import Callable, Iterable&lt;br&gt;
from typing import Generic, TypeVar&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Let's define a few classes, the same as we used with covariance so we can compare the two. We will use &lt;code&gt;Animal&lt;/code&gt; as a base class, and define &lt;code&gt;Cat&lt;/code&gt; and &lt;code&gt;Dog&lt;/code&gt; as subclasses of Animal.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`python&lt;br&gt;
class Animal:&lt;br&gt;
    &lt;a class="mentioned-user" href="https://dev.to/abstractmethod"&gt;@abstractmethod&lt;/a&gt;&lt;br&gt;
    def say(self) -&amp;gt; str: ...&lt;/p&gt;

&lt;p&gt;class Cat(Animal):&lt;br&gt;
    def say(self) -&amp;gt; str:&lt;br&gt;
        return "Meow"&lt;/p&gt;

&lt;p&gt;class Dog(Animal):&lt;br&gt;
    def say(self) -&amp;gt; str:&lt;br&gt;
        return "Woof"&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Example: Function Arguments
&lt;/h2&gt;

&lt;p&gt;Let's see how this works with function arguments for callables. Callables are generic types that are contravariant in their argument types, e.g. you can assign an instance of &lt;code&gt;Callable[[Animal], None]&lt;/code&gt; to a variable of type &lt;code&gt;Callable[[Cat], None]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We can define a few setter functions that takes an argument of type &lt;code&gt;Animal&lt;/code&gt; or &lt;code&gt;Cat&lt;/code&gt; and returns nothing. These are the opposites of the &lt;code&gt;getter&lt;/code&gt; functions we defined for covariance.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`python&lt;br&gt;
def set_animal(animal: Animal) -&amp;gt; None:&lt;br&gt;
    pass&lt;/p&gt;

&lt;p&gt;def set_animals(animals: Iterable[Animal]) -&amp;gt; None:&lt;br&gt;
    pass&lt;/p&gt;

&lt;p&gt;def set_cat(cat: Cat) -&amp;gt; None:&lt;br&gt;
    pass&lt;/p&gt;

&lt;p&gt;def set_cats(cats: Iterable[Cat]) -&amp;gt; None:&lt;br&gt;
    pass&lt;/p&gt;

&lt;p&gt;def get_animal() -&amp;gt; Animal:&lt;br&gt;
    return Cat()  # Cat, but returned as Animal&lt;/p&gt;
&lt;h1&gt;
  
  
  Cat -&amp;gt; Animal
&lt;/h1&gt;

&lt;p&gt;var1: Animal = Cat()  # Ok, polymorphism&lt;/p&gt;
&lt;h1&gt;
  
  
  This works because a function that takes a Cat is compatible with a function that
&lt;/h1&gt;
&lt;h1&gt;
  
  
  takes an Animal.
&lt;/h1&gt;

&lt;p&gt;var2: Callable[[Cat], None] = set_animal  # Ok, since Callable is contravariant for arguments&lt;br&gt;
var3: Callable[[Iterable[Cat]], None] = set_animals  # Ok, contravariance&lt;br&gt;
var4: Callable[[], Cat] = get_animal  # Error: "Animal" is incompatible with "Cat"&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We start in a similar way as we did with covariance. We see that for the first assignment, everything is ok, since the type of &lt;code&gt;var1&lt;/code&gt; is &lt;code&gt;Animal&lt;/code&gt;, which is a base class of &lt;code&gt;Cat&lt;/code&gt;. This is normal polymorphism.&lt;/p&gt;

&lt;p&gt;For the second and third assignments, we start to see how contravariance works for function arguments. This works because a function that takes an &lt;code&gt;Animal&lt;/code&gt; can be assigned to a variable that is annotated as a callable that takes a &lt;code&gt;Cat&lt;/code&gt;. We can always call a callback that takes an &lt;code&gt;Animal&lt;/code&gt; with a &lt;code&gt;Cat&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For the last assignment, we get an error, since &lt;code&gt;get_animal&lt;/code&gt; is a function that returns an &lt;code&gt;Animal&lt;/code&gt;, and &lt;code&gt;get_animal&lt;/code&gt; is not compatible with a function that returns a &lt;code&gt;Cat&lt;/code&gt;. This is because callables are not contravariant on return types.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`python&lt;/p&gt;
&lt;h1&gt;
  
  
  Animal -&amp;gt; Cat
&lt;/h1&gt;

&lt;p&gt;var5: Cat = Animal()  # Error: "Animal" is incompatible with "Cat"&lt;/p&gt;
&lt;h1&gt;
  
  
  We get an error here because a function that takes an Animal is not compatible with
&lt;/h1&gt;
&lt;h1&gt;
  
  
  a function that takes a Cat. This is because the function might take a Dog.
&lt;/h1&gt;

&lt;p&gt;var6: Callable[[Animal], None] = set_cat  # Error: ...&lt;br&gt;
var7: Callable[[Iterable[Animal]], None] = set_cats  # Error: ...&lt;/p&gt;
&lt;h1&gt;
  
  
  (*) Type parameter "_T_co@Iterable" is covariant, but "Animal" is not a subtype of "Cat"
&lt;/h1&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For the first assignment, we get an error, since &lt;code&gt;Animal&lt;/code&gt; is not a subtype of &lt;code&gt;Cat&lt;/code&gt;. For the second and third assignments, we get an error because &lt;code&gt;Animal&lt;/code&gt; is not a subtype of &lt;code&gt;Cat&lt;/code&gt;. If you think about the callable as a callback, then it's easier to see that you cannot give an &lt;code&gt;Animal&lt;/code&gt; e.g. a &lt;code&gt;Dog&lt;/code&gt; to a function that takes a &lt;code&gt;Cat&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;
  
  
  Custom Contravariant Generic Classes
&lt;/h1&gt;

&lt;p&gt;We can also define our own contravariant generic classes, similar to how we made covariant classes. To define a contravariant generic class, we need to use the &lt;code&gt;contravariant&lt;/code&gt; keyword argument for the &lt;code&gt;T_contra&lt;/code&gt; type variable.` This is by the way similar to how we declared type variables before Python 3.12.&lt;/p&gt;

&lt;p&gt;We will make a &lt;code&gt;Rabbit&lt;/code&gt; class that is a subclass of &lt;code&gt;Animal&lt;/code&gt;, and a &lt;code&gt;Hat&lt;/code&gt; class that is contravariant in its type parameter. This means that a &lt;code&gt;Hat[Animal]&lt;/code&gt; is a subtype of &lt;code&gt;Hat[Rabbit]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1738417782922%2F4bac24f9-c168-47fb-9ba0-5fe6a81c781c.png%2520align%3D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1738417782922%2F4bac24f9-c168-47fb-9ba0-5fe6a81c781c.png%2520align%3D" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's see how this looks in code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;T_contra&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TypeVar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;T_contra&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;contravariant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Rabbit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Squeak&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Hat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Generic&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T_contra&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;T_contra&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;T_contra&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Callable_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Generic&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T_contra&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;T_contra&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's see what happens if we try to add a method that returns the generic type for a class with a contravariant type variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InvalidHat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Hat&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T_contra&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;T_contra&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;T_contra&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# Error: Contravariant type variable cannot be used as a return type
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we see in the example above, we get an error when we try to add a method that returns the generic type. This is because contravariant types can only be used as function argument types. If a class were to allow returning values of the generic type, it could lead to type safety issues.&lt;/p&gt;

&lt;p&gt;One way to think about contravariance is that contravariant types are "in" types and can only be used as function argument types. If a class were to allow returning values of the generic type, it would violate the principle of contravariance and could lead to type safety issues.&lt;/p&gt;

&lt;p&gt;We see that we get the opposite of what we saw with covariance. The &lt;code&gt;get_value&lt;/code&gt; method now has an error, since we cannot return a value of type &lt;code&gt;T_contra&lt;/code&gt;. But the &lt;code&gt;set_value&lt;/code&gt; method works, since we can set the value to a value of type &lt;code&gt;T_contra&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Animal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Rabbit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;hat_with_rabbit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hat&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Rabbit&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Hat&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_animal_hat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Hat&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Hat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Rabbit&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;


&lt;span class="n"&gt;fetch_rabbit_hat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="p"&gt;[[],&lt;/span&gt; &lt;span class="n"&gt;Hat&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Rabbit&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fetch_animal_hat&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above we defined &lt;code&gt;Hat[Animal]&lt;/code&gt; and assign it to a variable that has the type &lt;code&gt;Hat[Rabbit]&lt;/code&gt;. This would have given an error if the generic type &lt;code&gt;T_contra&lt;/code&gt; was not contravariant.&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;Contravariance is the opposite of covariance, and this makes it quite a bit harder to understand since &lt;code&gt;Hat[Rabbit]&lt;/code&gt; is not a subtype of &lt;code&gt;Hat[Animal]&lt;/code&gt; perhaps as you might expect. It is actually the other way around. With contravariance &lt;code&gt;Hat[Animal]&lt;/code&gt; becomes a subtype of &lt;code&gt;Hat[Rabbit]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When we define a type to be contravariant, we are able to assign a container i.e. generic class of e.g. &lt;code&gt;Animal&lt;/code&gt;, to a variable that is annotated as a generic class of &lt;code&gt;Rabbit&lt;/code&gt;. This would not have been possible if the type was not contravariant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Invariance in Generics
&lt;/h2&gt;

&lt;p&gt;Invariance (in- = un/not) means that the type is not variant, and will not transform (vary) together with the wrapped type. This means that you can use only the type originally specified, and neither a more specific nor a more general type. This is the default behavior for generic types in Python.&lt;/p&gt;

&lt;p&gt;An invariant generic type parameter is neither covariant nor contravariant. You cannot assign an instance of &lt;code&gt;Hat[Animal]&lt;/code&gt; to a variable of type &lt;code&gt;Hat[Rabbit]&lt;/code&gt; or vice versa.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;abc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;abstractmethod&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Rabbit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Rabbit!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Hat&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;

    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RabbitHat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Hat&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Rabbit&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Rabbit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Putting &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; in the hat&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Rabbit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Pull a Rabbit out of the hat&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Rabbit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="c1"&gt;# This will not work due to invariance
&lt;/span&gt;&lt;span class="n"&gt;animal_hat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hat&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RabbitHat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Error: ...
# Type parameter "T@Hat" is invariant, but "Rabbit" is not the same as "Animal"
&lt;/span&gt;
&lt;span class="c1"&gt;# This also will not work due to invariance
&lt;/span&gt;&lt;span class="n"&gt;rabbit_hat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hat&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Rabbit&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Hat&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;]()&lt;/span&gt;  &lt;span class="c1"&gt;# Error: ...
# Type parameter "T@Hat" is invariant, but "Animal" is not the same as "Rabbit"
&lt;/span&gt;
&lt;span class="c1"&gt;# This is the only valid assignment
&lt;/span&gt;&lt;span class="n"&gt;rabbit_hat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hat&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Rabbit&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RabbitHat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# We can only put Rabbits in a RabbitHat
&lt;/span&gt;&lt;span class="n"&gt;rabbit_hat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Rabbit&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c1"&gt;# This will not work, even though Rabbit is a subclass of Animal
&lt;/span&gt;&lt;span class="n"&gt;rabbit_hat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;  &lt;span class="c1"&gt;# Error: ...
# "Animal" is incompatible with "Rabbit"
&lt;/span&gt;
&lt;span class="c1"&gt;# We can only take Rabbits from a RabbitHat
&lt;/span&gt;&lt;span class="n"&gt;rabbit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Rabbit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rabbit_hat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pull&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Invariance restricts us to use exactly the type specified. This happens when we use the generic type as both "in" and "out" types, meaning that methods of the type use the generic type both in the parameters, and return types.&lt;/p&gt;

&lt;p&gt;This hints that the generic type may be some kind of mutable container and we cannot allow assigning a &lt;code&gt;Hat[Animal]&lt;/code&gt; to a &lt;code&gt;Hat[Rabbit]&lt;/code&gt; or vice versa since that could easily lead to code adding a &lt;code&gt;Cat&lt;/code&gt; into a &lt;code&gt;Hat[Rabbit]&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)" rel="noopener noreferrer"&gt;Covariance and contravariance (computer science)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mypy.readthedocs.io/en/stable/generics.html#variance-of-generic-types" rel="noopener noreferrer"&gt;Variance in Python type checking (mypy docs)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance" rel="noopener noreferrer"&gt;Covariance and contravariance in generics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rednafi.com/python/variance_of_generic_types/" rel="noopener noreferrer"&gt;Variance of generic types in Python&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>tutorial</category>
      <category>typeannotations</category>
    </item>
  </channel>
</rss>
