Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Variable Types in a class
#1
When looking at a class, how do we know what the types of variables are? as an example:
class Node:
    def __init__(self, value, next_node=None, prev_node=None):
        self.value = value
        self.next = next_node
        self.prev = prev_node
How do we know that self.next is a "Node" type?

Thanks
buran write Oct-06-2022, 07:25 PM:
Please, use proper tags when post code, traceback, output, etc. This time I have added tags for you.
See BBcode help for more info.
Reply
#2
Python has a type() function for just that.

print(type(your_variable))
Reply
#3
(Oct-06-2022, 07:31 PM)XavierPlatinum Wrote: Python has a type() function for just that.

print(type(your_variable))

I realize you can get the type programmatically, but when simply reviewing the code to understand it, how can you tell what the type is?
Reply
#4
Use type annotations. This example is tricky since Node has not been defined. The convention for this case is to use a string.
class Node:
    def __init__(self, value:float, prev:'Node' = None, next:'Node' = None):
        self.value = value
        self.prev = prev
        self.next = next
You can also use typing_extensions.Self
from typing_extensions import Self

class Node:
    def __init__(self, value:float, prev:Self = None, next:Self = None):
        self.value = value
        self.prev = prev
        self.next = next
XavierPlatinum likes this post
Reply
#5
The values themselves have types, but the variables that refer to them do not. Python is dynamically typed, so a variable can refer to values of different types:

x = 1 # x refers to an integer
x = "foo" # now x refers to a string 
There are type hints - see the typing module, though as per the note in the docs, these aren't enforced.
Reply
#6
You could use a dataclass for this
from dataclasses import dataclass
from typing import Optional


@dataclass
class Node:
    value: float
    next: Optional["Node"] = None
    prev: Optional["Node"] = None
Reply
#7
Other than dataclasses you don't see typing used much for instance variables. This is likely because instance variables are really just entries in a dictionary. Since instance variables are really just dictionary entries, you can add instance variables at any time, not just in the __init__ method. Even dataclass objects allow adding attributes. Using Yoriz' example:
from dataclasses import dataclass
 
@dataclass
class Node:
    value: float
    next: "Node" = None
    prev: "Node" = None

x = Node(5)
x.string = "Hello"
print(x, x.string)
Output:
Node(value=5, next=None, prev=None) Hello
"string" is an attribute of x. x might be the only Node in history to have a "string" attribute, making x not only different from other Note objects by having a unique ID and different instance variable values, but also by having an additional instance variable.

If you want to restrict class instances to only having variables that you define, you can also use Pydantic. Defining a class using pydantic looks very, very similar to defining a dataclass.
from pydantic import BaseModel
 
class Node(BaseModel):
    value: float
    prev: "Node" = None
    next: "Node" = None

x = Node(value=5)
x.string = "Hello"
Error:
ValueError: "Node" object has no field "string"
Instead of adding a "string" attribute to x we get a ValueError. BaseModel overrides __setattr__() to raise an exception instead of adding the "string":"Hello" to the object dictionary (x.__dict__).

Another way to get very similar behavior is using __slots__.
class Node():
    __slots__ = ("value", "prev", "next")
    value: float
    prev: "Node"
    next: "Node"

    def __init__(self, value:float, prev:"Node" = None, next:"Node" = None):
        self.value = value
        self.prev = prev
        self.next = next

x = Node(value=5)
x.string = "Hello"
Error:
AttributeError: 'Node' object has no attribute 'string'
Here we get an AttributeError, which I think is a better choice than a ValueError. The error is raised not because something overrode the normal __setattr__() behavior to prevent adding items to the object dictionary, but because classes that define __slots__ don't have an object dictionary at all. When a class uses __slots__, all instances of the class have the same attributes. The attributes can have different values, but you cannot add any new instance variables.
Reply
#8
deanhystad - Your explanation of this issue is brilliant and very educational. I am most grateful for the time you took to explain it so clearly. Realizing "instance variables are really just entries in a dictionary" is foundational to understanding python. Not sure how you gained this deep insight into Python, but understanding it at these levels is required by all, especially me :)

Yoriz's suggestion of dataclass is also very helpful. Thank you Yoriz.
Reply
#9
If you are actively using a type checker the following code would flag an error
from dataclasses import dataclass
from typing import Optional


@dataclass
class Node:
    value: float
    next: Optional["Node"] = None
    prev: Optional["Node"] = None


node = Node(0.1)
node.string = "Hello"
For instance in vscode with the type checker mode set to at least basic, pylance would put a red line under string and hovering over it would show the following:
Error:
string: Literal['Hello'] Cannot assign member "string" for type "Node" Member "string" is unknown

Python version 3.10 added slots as a parameter to dataclass, The following can now be done
from dataclasses import dataclass
from typing import Optional


@dataclass(slots=True)
class Node:
    value: float
    next: Optional["Node"] = None
    prev: Optional["Node"] = None


node = Node(0.1)
node.string = "Hello"
this will give the same AttributeError: 'Node' object has no attribute 'string' error
Reply
#10
I'm not a fan of how decorators look but using @dataclass(slots=True) is so much cleaner than using __slots__. That is a great addition.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  can Inner Class reference the Outer Class's static variable? raykuan 6 5,939 Jul-01-2022, 06:34 AM
Last Post: SharonDutton
  Distinguishing different types of class attributes Drone4four 4 2,117 Feb-21-2022, 06:34 PM
Last Post: deanhystad
  Calling a base class variable from an inherited class CompleteNewb 3 1,700 Jan-20-2022, 04:50 AM
Last Post: CompleteNewb
  Can we access instance variable of parent class in child class using inheritance akdube 3 14,007 Nov-13-2020, 03:43 AM
Last Post: SalsaBeanDip
  Class variable / instance variable ifigazsi 9 4,332 Jul-28-2020, 11:40 AM
Last Post: buran
  Assignment of non-existing class variable is accepted - Why? DrZ 6 4,291 Jul-13-2020, 03:53 PM
Last Post: deanhystad
  Limiting valid values for a variable in a class WJSwan 5 3,666 Jul-06-2020, 07:17 AM
Last Post: Gribouillis
  Newbie: Help with variable selection in a class Slack86 5 2,747 Apr-07-2020, 08:41 PM
Last Post: jefsummers
  Using variable from a class in another .py script keegan_010 4 3,001 Mar-25-2020, 07:47 AM
Last Post: keegan_010
  How to access class variable? instances vs class drSlump 5 3,360 Dec-11-2019, 06:26 PM
Last Post: Gribouillis

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020