As far as I understand var
is a class variable here:
class MyClass:
var = 'hello'
def __init__(self):
print(self.var)
And thats an instance variable:
class MyClass:
def __init__(self, var):
self.var = var
print(self.var)
I had the problem, that I was looking for a method to make type hinting possible for instance variables. I can of course typehint the parameter with def __init__(self, var: str):
but that would not effect the instance variable itself.
Then I noticed in some descriptions (like here) that they used the term instance variable for a var
like this:
class MyClass:
var : str = 'hello'
def __init__(self, var : str = None):
self.var = var if var
print(self.var)
That would be the solution indeed, but is that still an instance variable? Because it is defined in the class body, it would be a class variable in my understanding. If you would use a list for var, all alterations to this list-var would be shared over the instances.
But in this case there would be no problem, because the string is replaced and would not be shared for other instances. However, it seems wrong to me if you call it an instance variable and I don't know if I should use it like this just to have the type hinting working.
Class methods cannot access instance variables or instance methods directly—they must use an object reference.
An instance variable is a variable that is specific to a certain object. It is declared within the curly braces of the class but outside of any method. The value of an instance variable can be changed by any method in the class, but it is not accessible from outside the class.
Instance variables are declared inside the constructor i.e., the __init__() method. Class variables are declared inside the class definition but outside any of the instance methods and constructors. It is gets created when an instance of the class is created. It is created when the program begins to execute.
Following are the notable differences between Class (static) and instance variables. Instance variables are declared in a class, but outside a method, constructor or any block. Class variables also known as static variables are declared with the static keyword in a class, but outside a method, constructor or a block.
That would be the solution indeed, but is that still an instance variable? Because it is defined in the class body, it would be a class variable in my understanding. [...snip...] However, it seems wrong to me if you call it an instance variable and I don't know if I should use it like this just to have the type hinting working.
For what it's worth, I also share the same discomfort. It seems like we're conceptually mixing two concepts there just for the sake of having cleaner type annotations.
However, I've asked Guido one or two times about this, and it seems like he does indeed prefers treating those class attributes as if they were instance attributes.
In any case, to answer your core question, if we do this:
class Test:
field1: int
field2: str = 'foo'
Then...
field1
field2
that has a default value of 'foo' (as per PEP 526).field1
to Test, but not a class attribute. (Class annotations are not automatically turned into class attributes.)field2
to Test as well as a class attribute named field2
containing the value 'foo'. So, it can get a bit muddled.
But regardless, this then begs the question: how do we indicate to a type checker that we want some field to genuinely be a class attribute?
Well, it turns out PEP 484 was amended semi-recently to contain the ClassVar
type annotation, which does exactly that.
So, if we wanted to add a new class attribute, we could do this:
from typing import ClassVar
class Test:
field1: int
field2: str = 'foo'
field3: ClassVar[int] = 3
So now, field3
should be treated as a class attribute with a default value of '3'.
(Note: ClassVar was added to typing
for Python 3.5.3 -- if you're using the older version of typing
bundled with Python 3.5, you can get a "backport" of the type by installing the typing_extensions
third part module via pip and importing ClassVar
from there instead.)
I think whether you decide to embrace this approach or not use it is a personal preference.
On one hand, Guido's opinion, pretty much by definition, defines what's "Pythonic" or not, so from that stance, there's no issue adopting this new idiom. Furthermore, the language itself is slowly but surely shifting to adopt this new idiom -- see the very recently accepted PEP 557, for example, which ends up following this same idiom of treating class attributes/class annotations as instance attributes.
On the other hand, it's difficult to shake off the nagging worry that this subtle difference will lead to issues down the line. In that case, you could stick with the standard approach of just setting all your fields inside __init__
. This approach also has the benefit of keeping your code compatible with Python 2 and 3.x - 3.5.
A middle ground might be to just simply never use class attributes, in any way, shape, or form, and just stick to using class annotations. This is slightly restrictive, since we can no longer give our instance variables default values, but we can now avoid conflating class attributes with instance attributes entirely. (As previously stated, and as pointed out in the comments, class annotations are not added as class attributes.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With