I read @ free_jinji2020's "I thought about why Python self is necessary as a beginner". I used another approach to imagine the internal structure and behavior of the Python interpreter, and actually verified the behavior with the Python interpreter, which deepened my understanding, so I will write down how to do it.
In addition, the operation check is done with Python3 series. Please note that the display result will be different in Python2 series.
When the Python interpreter reads a Python script and finds a " def
definition ", it creates an" instance of the function
class", makes it a "function object" that can call the process, and assigns it to the variable of the function name.
The processing in the function is compiled into Bytecode and assigned to the function object. You can also disassemble the bytecode (https://docs.python.org/ja/3.5/library/dis.html).
>>> def hello():
... print("Hello, world!")
...
>>> hello
<function hello at 0x6fffffd0dae8>
>>> type(hello)
<class'function'>
>>> hello()
Hello, world!
>>> hello.__code__.co_code.hex()
'740064018301010064005300'
>>> import dis
>>> dis.dis(hello)
2 0 LOAD_GLOBAL 0 (print)
2 LOAD_CONST 1 ('Hello, world!')
4 CALL_FUNCTION 1
6 POP_TOP
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
When the Python interpreter finds a " class
definition ", it instantiates a type
class, makes it a "class object", and assigns it to a variable in the class name.
A class object can create a ** namespace ** (** variable space **), define ** variables ** freely in the class object, and assign ** values ** and ** functions **. I will.
Variables in the object can be seen with the vars
function. You can also see the names in local scope with the dir
function. I also have Various information.
>>> class Sample:
... pass
...
>>> Sample.value = 123
>>> Sample.value * 3
369
>>> Sample.data = [1, 2, 3]
>>> vars(Sample)
mappingproxy({'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Sample' objects>, '__weakref__': <attribute '__weakref__' of 'Sample' objects>, '__doc__': None,
'value': 123, 'data': [1, 2, 3]})
>>> dir(Sample)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
>>> Sample.__name__
'Sample'
The function is not executed unless it is called, but the variable assignment (assignment to the class object) and function definition (assignment to the function object) written directly under the class definition are executed at the time of class definition.
>>> class Sample:
... value = 123
... print(value * 3)
...
369
>>> Sample.value
123
If there is a def
definition (function definition) in the class
definition, the "function object" is assigned in the class object.
It can be called with class name.function name ()
.
>>> class Sample:
... def greet():
... print("Hey, guys!")
...
>>> Sample
<class'__main__.Sample'>
>>> type(Sample)
<class'type'>
>>> Sample.greet
<function Sample.greet at 0x6fffffd0dd08>
>>> type(Sample.greet)
<class'function'>
>>> Sample.greet()
Hey, guys!
You can see that the greet
function object (function greet
) is assigned to the variable greet
.
>>> vars(Sample)
mappingproxy({'__module__': '__main__', 'greet': <function Sample.greet at 0x6fffffd0dd08>, '__dict__': <attribute '__dict__' of 'Sample' objects>, '__weakref__': <attribute '__weakref__' of 'Sample' objects>, '__doc__': None})
When creating a class object, the process called "constructor" is automatically set as __call__
method, and " You can now call the constructor with class name ()
".
Calling the constructor creates a "data area" called an "instance".
Instances can also create namespaces (variable spaces), freely define variables and assign values. The initial instance is empty with no variables.
>>> data = Sample()
>>> vars(data)
{}
>>> data.name = "Taro"
>>> vars(data)
{'name': 'Taro'}
Let's define a "function" that displays the name
assigned to the instance and display the name
.
You need an argument that tells which instance the name
is.
>>> def introduce(data):
... print("My name is", data.name)
...
>>> introduce(data)
My name is Taro
You can also call the ʻintroduce` function defined outside the class above by assigning it to a class object.
>>> introduce
<function introduce at 0x6fffffd0dc80>
>>> Sample.introduce = introduce
>>> Sample.introduce
<function introduce at 0x6fffffd0dc80>
>>> Sample.introduce(data)
My name is Taro
The "constructor" was automatically set when the class was created, but the "method" is automatically set when the instance is created. A "method" is an instance of the method class. There are various types of methods such as "instance method" and "class method". When you call a method, the function in the class object is called with the instance or class object as the first argument.
>>> Sample.introduce
<function introduce at 0x6fffffd0dc80>
>>> type(Sample.introduce)
<class'function'>
>>> data.introduce
<bound method introduce of <__main__.Sample object at 0x6fffffd16518>>
>>> type(data.introduce)
<class'method'>
>>> data.introduce()
My name is Taro
I assigned the ʻintroduce` function defined outside the class to the class object, but it is easier to understand if the function definition is written inside the class.
>>> class Person:
... def initialize(data, name):
... data.name = name
... def introduce(data):
... print("My name is", data.name)
...
>>> taro = Person()
>>> vars(taro)
{}
>>> Person.initialize(taro, "Taro")
>>> vars(taro)
{'name': 'Taro'}
>>> Person.introduce(taro)
My name is Taro
You can also call in-class functions via instance methods. As I wrote earlier, the method calls the function in the class object with the instance itself as the first argument.
>>> hanako = Person()
>>> hanako.inittialize("Hanako")
>>> hanako.introduce()
My name is Hanako
__init__
methodAfter creating the instance, the name is set by calling the ʻinitialize method, but it would be convenient if the initial value could be specified in the argument of the constructor. That is the [
initmethod](https://docs.python.org/ja/3.6/reference/datamodel.html#object.__init__). After the constructor instantiates, it calls the
init` method with the instance itself as the first argument. With the arguments passed to the constructor.
>>> class Person:
... def __init__(self, name):
... self.name = name
... def introduce(self):
... print("My name is", self.name)
...
>>> ichiro = Person("Ichiro")
>>> ichiro.introduce()
My name is Ichiro
self
In "PEP8: Python Code Style Guide", it says:
Always use
self
as the name of the first argument of the instance method. Always usecls
as the name of the first argument of the class method.
It doesn't matter what the argument name is, but let's change the argument name data
in the above code to self
according to the style guide.
That's the general way of writing.
@ classmethod
If you add a @ classmethod
decorator when defining an in-class function, it will be replaced with a method that calls the first argument as a class object.
You can define processing for each class that does not depend on the instance.
>>> class Sample:
... @classmethod
... def class_method(cls):
... print("called:", cls)
...
>>> Sample.class_method
<bound method Sample.class_method of <class'__main__.Sample'>>
>>> Sample.class_method()
called: <class'__main__.Sample'>
>>> s = Sample()
>>> s.class_method
<bound method Sample.class_method of <class'__main__.Sample'>>
>>> s.class_method()
called: <class'__main__.Sample'>
@staticmethod
If you add a @staticmethod
decorator when defining an in-class function, the function will be called directly without adding the first argument.
It's the same as a normal function definition, but by putting it in the class namespace, you can define functions with the same name in various classes, so you don't have to worry about name conflicts.
>>> class Sample:
... @staticmethod
... def static_method():
... print("Hello, world!")
...
>>> Sample.static_method
<function Sample.static_method at 0x6fffffd1e268>
>>> Sample.static_method()
Hello, world!
>>> s = Sample()
>>> s.static_method
<function Sample.static_method at 0x6fffffd1e268>
>>> s.static_method()
Hello, world!
Function objects are responsible for all processing in Python, and an argument is required to specify which data = instance = object to process, and the operation rule that the first argument name passed by the instance should be unified with self
. Was found to be specified in PEP8.
I haven't followed the source code of the Python interpreter in depth, so I think there are some inaccuracies. I would appreciate it if you could give me comments such as suggestions. There were many things I could understand by thinking about the feelings of the Python interpreter. Why don't you think about the feelings of the Python interpreter?
Recommended Posts