[Python] What are @classmethods and decorators?

What to write in this article

The notation @classmethod that appears in Python classes. Organize what this means, how to use it, and what is useful.

What is @classmethod?

What is a decorator?

To understand decorator expressions, you need to know about decorators. A decorator is a function ** that receives a function and returns a new one.

It can be created using the following properties of the function. ① Functions are assigned to variables ② You can define other functions within the function ③ The function can return the function ④ You can pass a function as an argument of a function

I will try to see if the above ① ~ ④. Are true ↓

① Functions are assigned to variables



#Function definition
def big(word="hello world"):
    return word.capitalize()+"!"
print(big()) # <- Hello world!

#Functions are objects, so you can assign them to variables and use them.
big2 = big
print(big2()) # Hello world!

② You can define other functions in the function


#Function nesting
def tension():
    x = 'by neko like man'
    # low()tenstion()Defined in
    def low(word='Hello World'):
        return word + '...' + x
    #Run immediately
    print(low())
    
# low()Is tension()Defined every time you call
tension() # <- Hello World...by neko like man

#But low()Is tension()Not accessible from outside
try:
    print(low())
except NameError:
    print('No low') # <- No low

The low function is defined inside the tension function. Also, the variable x defined in the tension function can be used in the internal function low. At this time, the low function is called a closure and the tension function is called an enclosure. Also, variables such as variable x that are used in a certain code block but are not defined in that code block are free variables (free variable. It is called 3 / reference / executionmodel.html # naming-and-binding)). (It is basically not possible to change the value of variable x from an internal function (it is possible by using nonlocal))

③ The function can return the function


def dragonball():
    #Free variables x and y
    x = 100
    y = 100
    # fusiton()Then the outer function(dragonball())X defined in,Holding y
    # fusion()Is a closure, dragonball()Is the enclosure
    def fusion(left='goku', light='trunks'):
        #If you want to update the value of a free variable from within the closure, nonlocal()use
        nonlocal x
        x = 900
        return f'{left}"Fu..." strength:{x} \
        {light}"Fu...." strength:{y} \
        {left}, {light}"John !!" Strength:{x+y}'
    #Defined in the dragonball function and returned.()Returns a function object without.
    return fusion

x = dragonball()
print(x())
#↑ goku "Fu..." strength:900 trunks "Fu"...." strength:100  goku, trunks「ジョン!!" strength:1000
print(x('piccolo','kuririn'))
#↑ piccolo "Fu..." strength:900 kuririn "Fu...." strength:100  piccolo, kuririn「ジョン!!" strength:1000

The function dragonball () returns the internally defined function fusion. By returning a function object without (), the variable x passed the return value ofdragonball ()can usefusion ().

④ A function can be passed as an argument of a certain function


def hand_over(func):
    print('hand over')
    print(func())
    
hand_over(dragonball())
# ↑ hand over
#goku "Fu..." strength:900 trunks "Fu"...." strength:100         goku, trunks「ジョン!!" strength:1000

You can pass the function dragonball as an argument to the function hand_over. The function dragonball contains the internal function fusion, and the function hand_over is passed theinternal function fusion. In addition, the function that can pass the function (in this case,hand_over ()`) does not work by itself because other functions may exist.


As mentioned above, the function ① Functions are assigned to variables ② You can define other functions within the function ③ The function can return the function ④ You can pass a function as an argument of a function It has four properties such as.

The decorator is a function defined using the properties of (2), (3), and (4) above, and is used using the properties of (1).

In other words, what is a decorator? ** A function that takes function A, adds functionality, and returns a new function A_ver2.0. The function A_ver2.0 is used by assigning it to a variable. ** **

When making a decorator

As a result, the function A_ver2.0 with added functions is born, and it can be used in the form of X (). (I thought it was similar to the class Mixin.)

Try using a decorator

How to make a decorator

def decorator name(Where to put the decorated function):
    #Make it a variadic argument so that it can handle any argument of the function to be decorated
def decorator(*args, **kwargs): 
        #Call the function to be decorated (you can use the original function as it is or edit it)
        result = func(*args, **kwargs)
        #Functions you want to add by decorating
return decorator# <-Includes decorated features

def Decorated function()
function

How to use the decorator_1

Variable to store the new decorated function=Decorator (function to be decorated)
Variable to store the new decorated function()

Practice

Suppose you have a function that lists and displays what you want to buy.

Function get_fruits


def get_fruits(*args):
    basket = []
    for i in args:
        basket.append(i)
    print(basket)
    

get_fruits('apple','banana') # <- ['apple', 'banana']

I will decorate this with the tweets in my heart.

Function get_fruits_ver2.0


#Decorator
def deco(func): #④
    def count(*args): #②
        print('What to buy')
        func(*args)
        print('Alright, let's do this')
    return count #③

def get_fruits(*args):
    basket = []
    for i in args:
        basket.append(i)
    print(basket)

# get_fruits()Deco()Decorate with
#Pass the decorator argument with the function you want to decorate to a variable
#If you pass an argument to that variable and execute it, deco+ get_fruits function (get_fruits_ver2.0) is executed
deco_get_fruits = deco(get_fruits) #①
deco_get_fruits('apple','banana')
#↑ What to buy
#   ['apple', 'banana']
#Alright, let's do this

By putting the object of the function created using the decorator in the variable (deco_get_fruits), the function addition version of get_fruits can be used. Also, by storing the object of the function created using the decorator in the variable name with the same name as the decorated function, it can be overwritten by the decorated function.


get_fruits = deco(get_fruits)
get_fruits('apple','banana')
#↑ What to buy
#   ['apple', 'banana']
#Alright, let's do this

As mentioned above, the function ver2.0 is created by assigning the return value of the decorator to the variable, but if you use the decorator expression, you do not have to assign it to the variable one by one, and the code will be clean. ..

How to use the decorator_2: Use the decorator formula

@Decorator name
def Decorated function()
function

Decorated function()

Practice

@deco
def get_vegetable(*args):
    basket = []
    for i in args:
        basket.append(i)
    print(basket)
    
get_vegetable('tomato','carrot')
#↑ What to buy
#   ['tomato', 'carrot']
#Alright, let's do this

By writing @ decorator name in the previous line of the function definition, the function defined directly below is decorated and assigned to the variable name with the same name as that function. In other words, the contents of the variable will be overwritten by the function ver_2.0. Synonymous with the code below.

#A variable with the same name as the decorated function name=Decorator (function to be decorated)
get_vegitable = deco(get_vegetable)

Usage_2 is easier to see than Usage_1. In this way, the processing content is the same, but the syntax is simple and easy to read. It is called ** Syntax Sugar **.

As mentioned above, using the decorator expression seems to improve readability by omitting the assignment to the variable. The main subject of this article, @ classmethod, is also a decorator expression. We are calling the built-in function classmethod (). classmethod () has a function to convert the method defined in the class into a class method.

classmethod classmethod is a method that can be used without creating an instance of the class. It seems to be used when you want to create an instance after processing inside the class in advance.

Concrete example

For example, in the detetime module, the today method of the date class is defined as a classmethod, which converts the elapsed time from the epoch (January 1, 1970 0:00:00) to local time and requires the information ( Year, month, day) is to be returned.

datetime module today method of date class


class date:
 #Abbreviation
    @classmethod
    def fromtimestamp(cls, t):
        "Construct a date from a POSIX timestamp (like time.time())."
        y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)
        return cls(y, m, d)

    @classmethod
    def today(cls):
        "Construct a date from time.time()."
        t = _time.time()
        return cls.fromtimestamp(t)
Personal study named commentary

In order to understand the above code, I will reorganize it in sentences.

The today class method uses the time function of the built-in module time called as _time to assign the elapsed time from the epoch (which returned the value 1576760183.8697512 when writing this blog) to the variable t. .. I'm passing it as an argument to the fromtimestamp class method.

In the fromtimestamp class method, the variable t is put in the localtime () function of the time module. If you use localtime (), the numbers in the variable t will be sorted into months, days, seconds, etc. and stored in tuples, so it is easy to understand and convenient. (If you use localtime () with no arguments, it will get the local time.)

time.struct_time(tm_year=2019, tm_mon=12, tm_mday=19, tm_hour=12, tm_min=56, tm_sec=41, tm_wday=3, tm_yday=353, tm_isdst=0)

Each of these is stored in the variables y, m, d, hh, mm, ss, weekday, jday, dst, and finally only y, m, d is returned.

So you can get the current year y month m day d by running the today class method.

What is useful

As an inexperienced person, I haven't grasped the merits of classmethod, but personally, "I want to initialize the class, but the code becomes complicated, so initialize it in a place other than __init__. I arranged it so that it would be used when I wanted to. Looking at the practical example, it seems that the process of acquiring information from the outside is defined in the class method.

I played with @classmethod.

When I was thinking about the code to get information from the outside, I wrote [Blog](http://atc.hateblo.jp/entry/2018/06/26/%E6%AF%8E%E6%9C] % 9D% E8% 87% AA% E5% 8B% 95% E3% 81% A7% E9% 99% 8D% E6% B0% B4% E7% A2% BA% E7% 8E% 87% E3% 82% 92 % E6% 95% 99% E3% 81% 88% E3% 81% A6% E3% 81% 8F% E3% 82% 8C% E3% 82% 8B% E3% 83% 97% E3% 83% AD% E3 % 82% B0% E3% 83% A9% E3% 83% A0% E3% 82% 92% E4% BD% 9C), so I wrote the code to get the local weather forecast information, so I will re- I tried using it.

A game to guess the local weather forecast


import requests, xml.etree.ElementTree as ET, os

#A game to guess the probability of precipitation today
class Game(object):
  ch = []
  
  def __init__(self, am1, am2, pm1, pm2):
    self.am1 = am1
    self.am2 = am2
    self.pm1 = pm1
    self.pm2 = pm2
  
  @classmethod
  #Get the probability of precipitation every 6 hours from the web Southern Ibaraki
  def rain_percent(cls):
    r = requests.get('https://www.drk7.jp/weather/xml/08.xml')
    r.encoding = r.apparent_encoding
    root = ET.fromstring(r.text)
    area = root.findall(".//area[@id]")  #North and South
    south = area[1] #Nodes in the southern area
    info = south.findall('.//info[@date]') #7 days in the south
    today = info[0] #Nodes for today's minutes in the South
    period = today.findall('.//period[@hour]') 
    cls.ch = []
    for percent in period:
      cls.ch.append(percent.text)
    return cls.ch

  def quiz(self):
    print(f'Your answer-> [{self.am1}-{self.am2}-{self.pm1}-{self.pm2}] :Probability of precipitation today-> {Game.ch}')


play1 = Game(10,10,10,10)
play1.rain_percent()
play1.quiz() #Your answer-> [10-10-10-10] :Probability of precipitation today-> ['0', '10', '40', '50']

It is accessible from both class and instance ↓

Game.rain_percent()  # ['0', '10', '40', '50']
play1.rain_percent() # ['0', '10', '40', '50']

It is recommended that the first argument of the class method be cls, and when called asclass name.class method ()orinstance name.class method (), cls will have class name or The instantiated class is passed.

Summary

As mentioned above, what is @classmethod?

Recommended Posts

[Python] What are @classmethods and decorators?
What are you comparing with Python is and ==?
What are python tuples and * args after all?
[Python] Python and security-① What is Python?
Python a + = b and a = a + b are different
What are "sudo ln -s" and "ln -s"?
(Beginner) What are cores and threads?
Modules and packages in Python are "namespaces"
What are go mod, go get and go mod vendors?
What are Linux POSIX options and GNU options?
Python open and io.open are the same
About python decorators
What is python
About Python decorators
What is Python
Training data and test data (What are X_train and y_train?) ①
Training data and test data (What are X_train and y_train?) ②
What is "functional programming" and "object-oriented" in Python?
[Python] What are the two underscore (underscore) functions before?
[Mathematics] Let's visualize what are eigenvalues and eigenvectors
How python classes and magic methods are working.
What are you using when testing with Python?
What I think Python and Ruby (and languages whose types are not specified) are dung
[python] Compress and decompress
Python and numpy tips
[Python] pip and wheel
Batch design and python
[Python] What is Pipeline ...
Higher-order functions and decorators
Python iterators and generators
Python packages and modules
Vue-Cli and Python integration
Python classes are slow
Ruby, Python and map
What are environment variables?
python input and output
Python and Ruby split
The VIF calculated by Python and the VIF calculated by Excel are different .. ??
Python3, venv and Ansible
Python asyncio and ContextVar
[Python] What is virtualenv
What I learned about AI and machine learning using Python (4)
Python and pip commands are not available on CentOS (RHEL) 8
Introduction to Effectiveness Verification Chapters 4 and 5 are written in Python
[Python3] "A // B" and "math.floor (A / B)" are not always the same! ??
Programming with Python and Tkinter
Encryption and decryption with Python
Python: Class and instance variables
3-3, Python strings and character codes
Python 2 series and 3 series (Anaconda edition)
Python and hardware-Using RS232C with Python-
Python on Ruby and angry Ruby on Python
Python indentation and string format
Python real division (/) and integer division (//)
Install Python and Flask (Windows 10)
About python objects and classes
About Python variables and objects
Apache mod_auth_tkt and Python AuthTkt
What are environment variables? (Linux)
[Python] if __name__ == What is'__main__' :?
Understand Python packages and modules