The notation @classmethod
that appears in Python classes. Organize what this means, how to use it, and what is useful.
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
① 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.)
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
Variable to store the new decorated function=Decorator (function to be decorated)
Variable to store the new decorated function()
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. ..
@Decorator name
def Decorated function()
function
Decorated function()
@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.
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)
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.
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.
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.
As mentioned above, what is @classmethod?
Recommended Posts