見出し画像

Python資格取得への道のり 8日目

・*argsと*kwargsの併用

def menu(food, *args, **kwargs):
  print(food)
  print(args)
  print(kwargs)

menu('banana', 'apple', 'orange', entree='beef', drink='coffee')
#banana
#('apple', 'orange')
#{'entree': 'beef', 'drink': 'coffee'}

注意点としては、*(アスタリスク)の順序。
*argsを一番最後に持ってくるとエラーとなる。どこが紐付けられているのかを確認しつつ、記載していく。

・Docstrings

def menu(food, *args, **kwargs):
  """
  menu function with *******************************
  
  Args:
   food (str): **********************
   *args (str): **********************
   **kwargs (str): **********************
  
  Return:
     
  """
  print(food)
  print(args)
  print(kwargs)    
  

"""(コメントアウト)で囲んだ部分に関しては、関数や1つ1つの引数の説明などを記載できる。
その部分を「Docstrings」と言い、それを出力することもできる。

def menu(food, *args, **kwargs):
   """
  menu function with *******************************

  Args:
   food (str): **********************
   *args (str): **********************
   **kwargs (str): **********************

  Return:

  """
   print(food)
   print(args)
   print(kwargs)

print(menu.__doc__)

help(menu)

「print(menu.__doc__)」と「help(menu)」でコメントアウトした中身を出力することができる。

・関数内関数

def outer(a, b):

  def plus(c, d):
      return c + d

  r = plus(a, b)
  print(r)

outer(1, 2)
#3

関数内関数であると、外側の関数の中でしか定義されない関数として内側の関数が使用できる。例えば、

def outer(a, b):

  def plus(c, d):
      return c + d

  r1 = plus(a, b)
  r2 = plus(b, a)
  print(r1 + r2)

outer(1, 2)
#6

・クロージャー

def outer(a, b):

  def inner():
     return a + b

  return inner

print(outer(1, 2))
f = outer(1, 2)
print(f())
#3

「return inner」のように関数を実行せず、「オブジェクトのまま」返すことをクロージャーという。その他にも例として

def circle_area_func(pi):
  def circle_area(radius):
     return pi * radius * radius

  return circle_area

cal1 = circle_area_func(3.14)
cal2 = circle_area_func(3.141592)

print(cal1)
print(cal1(10))
print(cal2(10))

#<function circle_area_func.<locals>.circle_area at 0x7fed6a233670>
#314.0
#314.1592

cal1の状態だとpiに3.14が入った状態ですが、まだradiusが入っていなかったので、実数の面積を返すことができない。関数のオブジェクトを返す。

ここでcal1に引数を入れた「cal1(10)」にてradiusに10が入り、実数の面積を返すことができる。

変数が2つあり、さらに引数を関数定義の際に入れられない際に使える手法となる

・デコレーター

def add_num(a, b):
  return a + b

print('start')
r = add_num(10, 20)
print('end')

print(r)
#start
#end
#30

「r = add_num(10, 20)」の前後に他の処理を行いたい。

もしくはadd_numに何かの機能を取り付けたい場合、デコレーターを使用する。

def print_info(func):
  def wrapper(*args, **kwargs):
      print('start')
      r = func(*args, **kwargs)
      print('end')
      return r
  return wrapper

上記がデコレーターである。もちろんこれだけでは動かないので、他の情報も追記する。

def print_info(func):
  def wrapper(*args, **kwargs):
      print('start')
      r = func(*args, **kwargs)
      print('end')
      return r
  return wrapper

def add_num(a, b):
  return a + b

f = print_info(add_num)
print(f)
a = f(10, 20)
print(a)

#<function print_info.<locals>.wrapper at 0x7fcf909335e0>
#start
#end
#30

「f = print_info(add_num)」の実行でprint_info(func)のfuncにadd_numを代入した状態で、さらに関数内関数のwrapperはオブジェクトを返すので、この時点では「#<function print_info.<locals>.wrapper at 0x7fcf909335e0>」となる。

ここからfを実行するため「f()」とし、さらにゆくゆくはadd_numの引数となる10と20を引数に入れることで、実行結果が得られる。

10と20に関しては*argsでタプル化されてしまいますが、その結果「r = add_num(10, 20)となり、30が返される。

def print_info(func):
  def wrapper(*args, **kwargs):
      print('start')
      r = func(*args, **kwargs)
      print('end')
      return r
  return wrapper

@print_info
def add_num(a, b):
   return a + b

print(add_num(20, 10))
#start
#end
#30

「@print_info」という書き方でデコレーターを事前にやるテイとしてしまいます。デコレーターのfuncにはadd_numをa, bには20と10をいれ、*argsに代入していくというのが、下記のコードで実行される。

@print_info
def add_num(a, b):
   return a + b

print(add_num(20, 10))

・デコレーターの活用

def print_info(func):
  def wrapper(*args, **kwargs):
      print('start')
      r = func(*args, **kwargs)
      print('end')
      return r
  return wrapper

@print_info
def add_num(a, b):
   return a + b

print(add_num(20, 10))

@print_info
def sub_num(c, d):
   return c - d

print(sub_num(20, 10))
#start
#end
#30
#start
#end
#10

一度デコレーターを定義して仕舞えば、他の関数を用いても活用できる。

今日はここまで!

関数内関数、クロージャー、デコレーターとだんだん難しさが倍増していっている感じです、、、、

これをプロのPythonエンジニアやPythonで活躍されている方は理解していることにリスペクト。でもこれまだ基礎!応用ってどうなるんだ?

明日は「ラムダ」から

この記事が気に入ったらサポートをしてみませんか?