Using globals() to create class object - python

I'm new in programming so please don't kill me for asking stupid questions.
I've been trying to understand all that class business in Python and I got to the point where could not find answer for my question just by google it.
In my program I need to call a class from within other class based on string returned by function. I found two solutions: one by using getattr() and second one by using globals() / locals().
Decided to go for second solution and got it working but I'm really don't understand how it's working.
So there is the code example:
class Test(object):
def __init__(self):
print "WORKS!"
room = globals()['Test']
room()
type(room()) gives:
<class '__main__.Test'>
type(room) gives:
<type 'type'> # What????
It looks like room() is a class object, but shouldn't that be room instead of room()?
Please help me because it is a little bit silly if I write a code which I don't understand myself.

What happens here is the following:
class Test(object):
def __init__(self):
print "WORKS!"
room = globals()['Test']
Here you got Test as room the way you wanted. Verify this:
room is Test
should give True.
type(room()) gives:
<class '__main__.Test'>
You do one step an go it backwards: room() returns the same as Test() would - an instance of that class. type() "undoes" this step resp. gets the type of the object - this is, of course, Test.
type(room) gives:
<type 'type'> # What????
Of course - it is the type of a (new style) class. The same as type(Test).
Be aware, however, that for
In my program I need to call a class from within other class based on string returned by function. I found two solutions: one by using getattr() and second one by using globals() / locals().
it could be better to create an explicitly separate dict. Here you have full control over which objects/classes/... are allowed in that context and which are not.

First of all, I'd go with getattr instead.
In your example, room equals Test and is a class. Its type is type.
When you call room(), you instantiate Test, so room() evaluates to an instance of Test, whose type is Test.

Classes are objects too, in Python. All this does:
class Test(object):
def __init__(self):
print "WORKS!"
is create a class object and bind it to the name Test. Much as this:
x = []
creates a list object and binds it to the name x.
Test() isn't magic syntax for creating an instance. The Test is perfectly ordinary variable lookup, and the () is perfectly ordinary "call with empty arguments". It just so happens that calling a class will create an instance of that class.
If follows then that your problem of instantiating a class chosen based on having the name of the class as a string boils down to the much simpler problem of finding an object stored in a variable. It's exactly the same problem as getting that list bound to the name x, given the string "x". Once you've got a reference to the class in any old variable, you can simply call it to create your instance.
globals() returns a dictionary mapping the names of globals to their values. So globals()['Test'] will get you the class Test just as easily as globals()['x'] will get you the list. However it's usually not considered great style to use globals() like this; your module probably contains a large number of callables (including a bunch imported from other modules) that you don't want to be accidentally invoked if the function can be made to return their name. Given that classes are just ordinary objects, you can put them in a dictionary of your own making:
classes = {
'Test': Test,
'SomethingElse': Something,
...
}
This involves a bit more typing, but it's also easier to see what the intended usage is, and it gives you a bit more flexibility, since you can also easily pass this dictionary to other modules and have the instantiation take place elsewhere (you could do that with globals(), but then you're getting very weird).
Now, for the type(room) being type. Again, this is just a simple consequence of the fact that classes themselves are also objects. If a class is an object, then it should also be an instance of some class. What class is that? type, the "type of types". Much as any class defines the common behaviour of all its instances, the class type defines the common behaviour of all classes.
And just to make your brain hurt, type is an instance of itself (since type is also a class, and type is the class of classes). And it's a subclass of object (since all type instances are object instances, but not all object instances are type instances), and also an instance of object (since object is the root class of which everything is an instance).
You can generally ignore type as an advanced topic, however. :)

Related

When is an "instance" being created?

I've been reading articles about OOP with python, specifically this one.
The autor of that article has a description and then a code example:
The Python syntax to instantiate a class is the same of a function
call
>>> b = int()
>>> type(b)
<type 'int'>
By this I infer "instance" exist at the moment of the execution and not before. When you execute type(b) that's the instance of the class int().
But then I read this stack overflow answer:
Instance is a variable that holds the memory address of the Object.
Which makes me a little be confused about the term. So when I assign a variable at the moment of the execution the "instance" is created?
Finally this explanation in ComputerHope points to the fact that instances are the same as variable assigments:
function Animal(numlegs, mysound) {
this.legs = numlegs;
this.sound = mysound;
}
var lion = new Animal(4, "roar");
var cat = new Animal(4, "meow");
var dog = new Animal(4, "bark");
The Animal object allows for the number of legs and the sound the
animal makes to be set by each instance of the object. In this case,
all three instances (lion, cat, and dog) have the same number of legs,
but make different sounds.
Could anyone actually provide a clear definition of when an instance exits?
I've been reading articles about OOP with python, specifically this
one.
The autor of that article has a description and then a code example:
The Python syntax to instantiate a class is the same of a function
call
>>> b = int()
>>> type(b)
<type 'int'>
Also read the sentence before that:
Once you have a class you can instantiate it to get a concrete object (an instance) of that type, i.e. an object built according to the structure of that class.
So an instance of a class is an object that has that class as its type.
By this I infer "instance" exist at the moment of the execution and
not before.
Yes, correct. "Instance" and "instance of" are runtime concepts in Python.
When you execute type(b) that's the instance of the
class int().
Not quite.
The int instance here starts existing when int() is called.1 This process is what's called "instantiation" and the result (which is returned by this call, and in this example then assigned to b) is the "instance" of int.
But then I read this stack overflow answer:
Instance is a variable that holds the memory address of the Object.
Oh well, that's not quite correct. It's the object itself (the value at that memory address, if you will) that's the instance. Several variables may be bound to the same object (and thus the same instance). There's even an operator for testing that: is
>>> a = 5
>>> b = a
>>> a is b
True
Which makes me a little be confused about the term. So when I assign a
variable at the moment of the execution the "instance" is created?
No, then the instance is bound to that variable. In Python, think of variables just as "names for values". So binding an object to a variable means giving that object that name. An object can have several names, as we saw above.
You can use an instance without assigning it to any variable, i.e., without naming it, e.g. by passing it to a function:
>>> print(int())
0
Finally this explanation in ComputerHope points to the fact that
instances are the same as variable assigments:
function Animal(numlegs, mysound) {
this.legs = numlegs;
this.sound = mysound;
}
var lion = new Animal(4, "roar");
var cat = new Animal(4, "meow");
var dog = new Animal(4, "bark");
The Animal object allows for the number of legs and the sound the
animal makes to be set by each instance of the object. In this case,
all three instances (lion, cat, and dog) have the same number of legs,
but make different sounds.
Unfortunately, that explanation on ComputerHope will probably confuse most readers more than it helps them. First, it conflates the terms "class" and "object". They don't mean the same. A class is a template for one type of objects. Objects and templates for a type of objects aren't the same concept, just as cookie cutters aren't the same things as cookies.
Of course, [for the understanding] it doesn't particularly help that in Python, classes are (special, but not too special) objects (of type type) and that in JavaScript until the class concept was introduced, it was customary to use plain objects as templates for other objects. (The latter approach is known as "prototype based object orientation" or "prototype based inheritance". In contrast, most other object oriented languages, including Python, use class-based object orientation / class-based inheritance. I'm not quite sure in what category modern ECMAScript with the class keyword falls.)
Could anyone actually provide a clear definition of instance?
Like I wrote further up:
An instance of a class is an object that has that class as its type.
So an "instance" is always an "instance of" something. That also answers the linguistic take on the question in the title
When should I call it “instance”?
You should call it "instance" when you want to call it "instance of" something (usually of a class).
1 I haven't told the whole truth. Try this:
>>> a = int()
>>> b = int()
>>> a is b
True
Wait what? Shouldn't the two invocations of int have returned new instances each, and thus two distinct ones?
That what would have happened with most types, but some built-in types are different, int being one of them. The makers of the CPython implementation are aware that small integers are used a lot. Thus they let CPython create new ones all the time, they just have it re-use the same integer (the same object / instance) each time the same value is required. Because Python integers are immutable, that doesn't usually cause any problems, and saves a lot of memory and object-creation-time in computation-intensive programs.
The Python standard allows implementations to make this optimization, but AFAIK doesn't require them to. So this should be considered an implementation detail and your program logic should never rely on this. (Your performance optimizations may rely on it, though.)
Generally in OOP
Classes and objects are the two main aspects of object oriented
programming. A class creates a new type where objects are instances of
the class.
As explained here.
Thus everytime an object is created, it is called an instance.
Python makes no difference in this concept, however things are a little different from other languages like Java for instance.
In fact in Python everything is an object, even classes themselves.
Here is a brief explanation of how it works:
Considering this snippet:
>>> class Foo:
... pass
...
>>> type(Foo)
<type 'type'>
>>>
Class Foo is type type which it is a metaclass for all classes in Python
(There is however a distinction between 'old' and 'new' classes, more here, here and here).
Class type being a class, is an instance of itself:
>>> isinstance(type, type)
True
So Foo despite being a class definition, is treated like an object by the interpreter.
Objects as instances are created whit statements like foo = Foo(), foo being an object inherits from object class.
>>> isinstance(foo, object)
True
This class provides all the methods an object needs, such as __new__() and __int__() (new, init). In short the former is used to create a new instance of a class, the latter is called after the instance has been created and is used to initialize values like you did with Animal.
The fact that everything is an object also mean that we can do funny pieces of code like this one:
>>> class Foo:
... var = 'hello'
...
>>> foo = Foo()
>>> foo.var
'hello'
>>> foo.other_var = 'world'
>>> foo.other_var
'world'
>>> Foo.other_var
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'Foo' has no attribute 'other_var'
>>> Foo.var
'hello'
>>>
Here I added an attribute on an object at runtime. That attribute will be unically in foo, the class itself or any other instances won't have it.
This is called Instance variable and class variable.
Hope it all makes sense to you.
TL;DR
In Python everything (class definitions, functions, modules, etc..) are all treated like objects by the interpreter. Therefore 'everything' is an instance.

Python 3 type is both an object and class?

I am reading Learning python. Regarding metaclass, the book said type is an object and also is itself class. I am trying to search python doc online but don't see any similar description. Can someone point me any official doc?
Thinking a bit more on this statement that type is both object and class, I think it makes sense.
The reasons are as following.
type is callable, seeing type(). Then it should be either a function or object which has implemented call
any object is instanced from a class. Given it's in the top hierarchy, it makes sense that its class is itself.
My second question is that metaclass is subclass of type class? Is it still true that metaclass is also an instance of type object/class? Because when Metaclass(...) is invoked, type.call is invoked then Metaclass must be instance of type
Even as a callable, type has two completely different uses. type with one parameter returns the class of an objecdt, while type with 3 arguments creates a new class, which is itself an object of the class type.
Of course, by that logic, type itself could be an instance of another class, and that class an instance of another class. But this had to stop somewhere. The design choice is that it stops there. There is no meta-metaclass. type(type) returns type. This is magic, in the sense that it does not follow from any logical rules, it is just specially implemented.

How does attribute resolution work in Python?

Consider the following code:
class A(object):
def do(self):
print self.z
class B(A):
def __init__(self, y):
self.z = y
b = B(3)
b.do()
Why does this work? When executing b = B(3), attribute z is set. When b.do() is called, Python's MRO finds the do function in class A. But why is it able to access an attribute defined in a subclass?
Is there a use case for this functionality? I would love an example.
It works in a pretty simple way: when a statement is executed that sets an attribute, it is set. When a statement is executed that reads an attribute, it is read. When you write code that reads an attribute, Python does not try to guess whether the attribute will exist when that code is executed; it just waits until the code actually is executed, and if at that time the attribute doesn't exist, then you'll get an exception.
By default, you can always set any attribute on an instance of a user-defined class; classes don't normally define lists of "allowed" attributes that could be set (although you can make that happen too), they just actually set attributes. Of course, you can only read attributes that exist, but again, what matters is whether they exist when you actually try to read them. So it doesn't matter if an attribute exists when you define a function that tries to read it; it only matters when (or if) you actually call that function.
In your example, it doesn't matter that there are two classes, because there is only one instance. Since you only create one instance and call methods on one instance, the self in both methods is the same object. First __init__ is run and it sets the attribute on self. Then do is run and it reads the attribute from the same self. That's all there is to it. It doesn't matter where the attribute is set; once it is set on the instance, it can be accessed from anywhere: code in a superclass, subclass, other class, or not in any class.
Since new attributes can be added to any object at any time, attribute resolution happens at execution time, not compile time. Consider this example which may be a bit more instructive, derived from yours:
class A(object):
def do(self):
print(self.z) # references an attribute which we have't "declared" in an __init__()
#make a new A
aa = A()
# this next line will error, as you would expect, because aa doesn't have a self.z
aa.do()
# but we can make it work now by simply doing
aa.z = -42
aa.do()
The first one will squack at you, but the second will print -42 as expected.
Python objects are just dictionaries. :)
When retrieving an attribute from an object (print self.attrname) Python follows these steps:
If attrname is a special (i.e. Python-provided) attribute for objectname, return it.
Check objectname.__class__.__dict__ for attrname. If it exists and is a data-descriptor, return the descriptor result. Search all bases of objectname.__class__ for the same case.
Check objectname.__dict__ for attrname, and return if found. If objectname is a class, search its bases too. If it is a class and a descriptor exists in it or its bases, return the descriptor result.
Check objectname.__class__.__dict__ for attrname. If it exists and is a non-data descriptor, return the descriptor result. If it exists, and is not a descriptor, just return it. If it exists and is a data descriptor, we shouldn't be here because we would have returned at point 2. Search all bases of objectname.__class__ for same case.
Raise AttributeError
Source
Understanding get and set and Python descriptors
Since you instanciated a B object, B.__init__ was invoked and added an attribute z. This attribute is now present in the object. It's not some weird overloaded magical shared local variable of B methods that somehow becomes inaccessible to code written elsewhere. There's no such thing. Neither does self become a different object when it's passed to a superclass' method (how's polymorphism supposed to work if that happens?).
There's also no such thing as a declaration that A objects have no such object (try o = A(); a.z = whatever), and neither is self in do required to be an instance of A1. In fact, there are no declarations at all. It's all "go ahead and try it"; that's kind of the definition of a dynamic language (not just dynamic typing).
That object's z attribute present "everywhere", all the time2, regardless of the "context" from which it is accessed. It never matters where code is defined for the resolution process, or for several other behaviors3. For the same reason, you can access a list's methods despite not writing C code in listobject.c ;-) And no, methods aren't special. They are just objects too (instances of the type function, as it happens) and are involved in exactly the same lookup sequence.
1 This is a slight lie; in Python 2, A.do would be "bound method" object which in fact throws an error if the first argument doesn't satisfy isinstance(A, <first arg>).
2 Until it's removed with del or one of its function equivalents (delattr and friends).
3 Well, there's name mangling, and in theory, code could inspect the stack, and thereby the caller code object, and thereby the location of its source code.

Classes How I understand them. Correct me if Im wrong please

I really hope this is not a question posed by millions of newbies, but my search didn t really give me a satisfying answer.
So my question is fairly simple. Are classes basically a container for functions with its own namespace? What other functions do they have beside providing a separate namespace and holding functions while making them callable as class atributes? Im asking in a python context.
Oh and thanks for the great help most of you have been!
More importantly than functions, class instances hold data attributes, allowing you to define new data types beyond what is built into the language; and
they support inheritance and duck typing.
For example, here's a moderately useful class. Since Python files (created with open) don't remember their own name, let's make a file class that does.
class NamedFile(object):
def __init__(self, name):
self._f = f
self.name = name
def readline(self):
return self._f.readline()
Had Python not had classes, you'd probably be working with dicts instead:
def open_file(name):
return {"name": name, "f": open(name)}
Needless to say, calling myfile["f"].readline() all the time will cause your fingers to hurt at some point. You could of course introduce a function readline in a NamedFile module (namespace), but then you'd always have to use that exact function. By contrast, NamedFile instances can be used anywhere you need an object with a readline method, so it would be a plug-in replacement for file in many situation. That's called polymorphism, one of the biggest benefits of OO/class-based programming.
(Also, dict is a class, so using it violates the assumption that there are no classes :)
In most languages, classes are just pieces of code that describe how to produce an object. That's kinda true in Python too:
>>> class ObjectCreator(object):
... pass
...
>>> my_object = ObjectCreator()
>>> print my_object
<__main__.ObjectCreator object at 0x8974f2c>
But classes are more than that in Python. Classes are objects too.
Yes, objects.
As soon as you use the keyword class, Python executes it and creates an OBJECT. The instruction:
>>> class ObjectCreator(object):
... pass
...
creates in memory an object with the name ObjectCreator.
This object (the class) is itself capable of creating objects (the instances), and this is why it's a class.
But still, it's an object, and therefore:
you can assign it to a variable
you can copy it
you can add attributes to it
you can pass it as a function parameter
e.g.:
>>> print ObjectCreator # you can print a class because it's an object
<class '__main__.ObjectCreator'>
>>> def echo(o):
... print o
...
>>> echo(ObjectCreator) # you can pass a class as a parameter
<class '__main__.ObjectCreator'>
>>> print hasattr(ObjectCreator, 'new_attribute')
False
>>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class
>>> print hasattr(ObjectCreator, 'new_attribute')
True
>>> print ObjectCreator.new_attribute
foo
>>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable
>>> print ObjectCreatorMirror.new_attribute
foo
>>> print ObjectCreatorMirror()
<__main__.ObjectCreator object at 0x8997b4c>
Classes (or objects) are used to provide encapsulation of data and operations that can be performed on that data.
They don't provide namespacing in Python per se; module imports provide the same type of stuff and a module can be entirely functional rather than object oriented.
You might gain some benefit from looking at OOP With Python, Dive into Python, Chapter 5. Objects and Object Oriented Programming or even just the Wikipedia article on object oriented programming
A class is the definition of an object. In this sense, the class provides a namespace of sorts, but that is not the true purpose of a class. The true purpose is to define what the object will 'look like' - what the object is capable of doing (methods) and what it will know (properties).
Note that my answer is intended to provide a sense of understanding on a relatively non-technical level, which is what my initial trouble was with understanding classes. I'm sure there will be many other great answers to this question; I hope this one adds to your overall understanding.

Python Variable Declaration

I want to clarify how variables are declared in Python.
I have seen variable declaration as
class writer:
path = ""
sometimes, there is no explicit declaration but just initialization using __init__:
def __init__(self, name):
self.name = name
I understand the purpose of __init__, but is it advisable to declare variable in any other functions?
How can I create a variable to hold a custom type?
class writer:
path = "" # string value
customObj = ??
Okay, first things first.
There is no such thing as "variable declaration" or "variable initialization" in Python.
There is simply what we call "assignment", but should probably just call "naming".
Assignment means "this name on the left-hand side now refers to the result of evaluating the right-hand side, regardless of what it referred to before (if anything)".
foo = 'bar' # the name 'foo' is now a name for the string 'bar'
foo = 2 * 3 # the name 'foo' stops being a name for the string 'bar',
# and starts being a name for the integer 6, resulting from the multiplication
As such, Python's names (a better term than "variables", arguably) don't have associated types; the values do. You can re-apply the same name to anything regardless of its type, but the thing still has behaviour that's dependent upon its type. The name is simply a way to refer to the value (object). This answers your second question: You don't create variables to hold a custom type. You don't create variables to hold any particular type. You don't "create" variables at all. You give names to objects.
Second point: Python follows a very simple rule when it comes to classes, that is actually much more consistent than what languages like Java, C++ and C# do: everything declared inside the class block is part of the class. So, functions (def) written here are methods, i.e. part of the class object (not stored on a per-instance basis), just like in Java, C++ and C#; but other names here are also part of the class. Again, the names are just names, and they don't have associated types, and functions are objects too in Python. Thus:
class Example:
data = 42
def method(self): pass
Classes are objects too, in Python.
So now we have created an object named Example, which represents the class of all things that are Examples. This object has two user-supplied attributes (In C++, "members"; in C#, "fields or properties or methods"; in Java, "fields or methods"). One of them is named data, and it stores the integer value 42. The other is named method, and it stores a function object. (There are several more attributes that Python adds automatically.)
These attributes still aren't really part of the object, though. Fundamentally, an object is just a bundle of more names (the attribute names), until you get down to things that can't be divided up any more. Thus, values can be shared between different instances of a class, or even between objects of different classes, if you deliberately set that up.
Let's create an instance:
x = Example()
Now we have a separate object named x, which is an instance of Example. The data and method are not actually part of the object, but we can still look them up via x because of some magic that Python does behind the scenes. When we look up method, in particular, we will instead get a "bound method" (when we call it, x gets passed automatically as the self parameter, which cannot happen if we look up Example.method directly).
What happens when we try to use x.data?
When we examine it, it's looked up in the object first. If it's not found in the object, Python looks in the class.
However, when we assign to x.data, Python will create an attribute on the object. It will not replace the class' attribute.
This allows us to do object initialization. Python will automatically call the class' __init__ method on new instances when they are created, if present. In this method, we can simply assign to attributes to set initial values for that attribute on each object:
class Example:
name = "Ignored"
def __init__(self, name):
self.name = name
# rest as before
Now we must specify a name when we create an Example, and each instance has its own name. Python will ignore the class attribute Example.name whenever we look up the .name of an instance, because the instance's attribute will be found first.
One last caveat: modification (mutation) and assignment are different things!
In Python, strings are immutable. They cannot be modified. When you do:
a = 'hi '
b = a
a += 'mom'
You do not change the original 'hi ' string. That is impossible in Python. Instead, you create a new string 'hi mom', and cause a to stop being a name for 'hi ', and start being a name for 'hi mom' instead. We made b a name for 'hi ' as well, and after re-applying the a name, b is still a name for 'hi ', because 'hi ' still exists and has not been changed.
But lists can be changed:
a = [1, 2, 3]
b = a
a += [4]
Now b is [1, 2, 3, 4] as well, because we made b a name for the same thing that a named, and then we changed that thing. We did not create a new list for a to name, because Python simply treats += differently for lists.
This matters for objects because if you had a list as a class attribute, and used an instance to modify the list, then the change would be "seen" in all other instances. This is because (a) the data is actually part of the class object, and not any instance object; (b) because you were modifying the list and not doing a simple assignment, you did not create a new instance attribute hiding the class attribute.
This might be 6 years late, but in Python 3.5 and above, you can give a hint about a variable type like this:
variable_name: type_name
or this:
variable_name # type: shinyType
This hint has no effect in the core Python interpreter, but many tools will use it to aid the programmer in writing correct code.
So in your case(if you have a CustomObject class defined), you can do:
customObj: CustomObject
See this or that for more info.
There's no need to declare new variables in Python. If we're talking about variables in functions or modules, no declaration is needed. Just assign a value to a name where you need it: mymagic = "Magic". Variables in Python can hold values of any type, and you can't restrict that.
Your question specifically asks about classes, objects and instance variables though. The idiomatic way to create instance variables is in the __init__ method and nowhere else — while you could create new instance variables in other methods, or even in unrelated code, it's just a bad idea. It'll make your code hard to reason about or to maintain.
So for example:
class Thing(object):
def __init__(self, magic):
self.magic = magic
Easy. Now instances of this class have a magic attribute:
thingo = Thing("More magic")
# thingo.magic is now "More magic"
Creating variables in the namespace of the class itself leads to different behaviour altogether. It is functionally different, and you should only do it if you have a specific reason to. For example:
class Thing(object):
magic = "Magic"
def __init__(self):
pass
Now try:
thingo = Thing()
Thing.magic = 1
# thingo.magic is now 1
Or:
class Thing(object):
magic = ["More", "magic"]
def __init__(self):
pass
thing1 = Thing()
thing2 = Thing()
thing1.magic.append("here")
# thing1.magic AND thing2.magic is now ["More", "magic", "here"]
This is because the namespace of the class itself is different to the namespace of the objects created from it. I'll leave it to you to research that a bit more.
The take-home message is that idiomatic Python is to (a) initialise object attributes in your __init__ method, and (b) document the behaviour of your class as needed. You don't need to go to the trouble of full-blown Sphinx-level documentation for everything you ever write, but at least some comments about whatever details you or someone else might need to pick it up.
For scoping purpose, I use:
custom_object = None
Variables have scope, so yes it is appropriate to have variables that are specific to your function. You don't always have to be explicit about their definition; usually you can just use them. Only if you want to do something specific to the type of the variable, like append for a list, do you need to define them before you start using them. Typical example of this.
list = []
for i in stuff:
list.append(i)
By the way, this is not really a good way to setup the list. It would be better to say:
list = [i for i in stuff] # list comprehension
...but I digress.
Your other question.
The custom object should be a class itself.
class CustomObject(): # always capitalize the class name...this is not syntax, just style.
pass
customObj = CustomObject()
As of Python 3, you can explicitly declare variables by type.
For instance, to declare an integer one can do it as follows:
x: int = 3
or:
def f(x: int):
return x
see this question for more detailed info about it:
Explicitly declaring a variable type in Python

Categories