728x90
반응형
!pip install multipledispatch​
xxx = 2

def x():
  xxx = 1 
  print(xxx)

x()
# 1

Python

Callable

Call: 함수를 호출 한다

Python에서 괄호('()') 붙일 있는 것은 callable 이다

, 호출 가능한 함수 또는 객체

1. function(method)

2. class

3. __call__이 정의된 object

LEGB

G: 기본적으로 사용하는 공간은 Global 공간 (global)

B: Builtin이라는 공간에 저장되어 있는 것들

E: Enclosing Function Local, 함수를 내포하는 다른 함수 영역

L: Local, 함수 정의된 지역변수

 

python 공간규칙

1. B에 없으면 G에 있는지 찾는다

2. 상위단계(B쪽으로 갈수록 상위)에서 하위단계로 접근할 없다

3. 하위단계(L쪽으로 갈수록 하위)에서 상위단계로는 접근할 있다

 

변수 찾는 우선순위 => L > E > G > B

xxx = 2

def x():
  xxx = 1 
  print(xxx)
x()
# 1
xxx = 2

def x():
  print(xxx)
x()
# 2

Closure

'함수를 중첩시키고 함수를 리턴하는 함수'. 클로저는 보통 함수를 쉽게 변형할 있는 기법이다 # Higher order function 클래스 안에 __call__이 선언되어 있으면 인스턴스가 호출가능해진다(인스턴스( )/__call__메소드가 호출된다)

사실 함수도 클래스의 객체이며 함수를 호출할 '함수이름( )' 같이 사용할 있었던 이유가 바로 __call__ 메서드가 선언되어 있기 때문이다

def clo(m):
    def inner(n):
        return m + n 
    return inner 

class A:
    def __init__(self, m):
        self.m = m
    def __call__(self, n):
        return self.m + n 
clo(4)(3)
# 7

a = A(3)
a(3)
# 6
import tensorflow as tf 
# 4개의 퍼셉트론에 tf.constant([[1,2],[3,4],[5,6]])값을 계산하는 함수 
tf.keras.layers.Dense(4)(tf.constant([[1,2],[3,4],[5,6]])) 

<tf.Tensor: shape=(3, 4), dtype=float32, numpy=
array([[ 1.57166   , -0.1644063 ,  1.6268642 , -0.73175406],
       [ 2.9206128 , -0.0049057 ,  3.2637818 , -1.5273063 ],
       [ 4.2695656 ,  0.1545949 ,  4.9006996 , -2.3228586 ]],
      dtype=float32)>
class B:
  def __init__(self, m):
    self.m = m
  
  def __call__(self, n):
    return self.m + n 
B(3)(4)
# 7

def __call__(self, n):
  return self.m + n + 1
B.__call__ = x

※ function이라는 데이터 타입도 존재한다

def a(n):
  return n 

type(a)
# function

Callable Method 종류

1. Instance method 2. classmethod 3. staticmethod

위임 개념은 크게 3가지로 볼 수 있다

1. instance 2. class 3. inheritance

 

class T:
  a = 1 # class variable/attribute 클래스 안에서는 식별자를 변수라고 칭한다 
        # attribute 클래스 안에 정의 되어 있는 모든 것을 attribute 라고 한다 
T.a # 클래스가 어떻게 행동해요? 클래스는 메타 클래스의 인스턴스이기 때문이다 
# 1

T.a # 클래스가 어떻게 행동해요? 클래스는 메타 클래스의 인스턴스이기 때문이다 
# {'xx': 1}

b.yy # 존재하지 않는 변수에 접근하면 attributeError를 발생시킨다 
# AttributeError: 'T' object has no attribute 'yy'

b.a # 인스턴스 변수에 없으면 클래스 변수에서 찾는다 (위임)
# 1
class A:
  @classmethod   
  def xx(cls):   # instance / class variable의 생성은 시점에 따라 결정된다 
    cls.t = 1 
    print('cls')
  
  def yy(self):
    print('instance')
 
A.xx # classmethod는 클래스 입장에서 함수가 메소드로 
# <bound method A.xx of <class '__main__.A'>>

A.yy
<function __main__.A.yy>

a = A()
a.xx() # 인스턴스에 없으면 클래스 찾는다
# cls

a.t
# 1
import pandas as pd 
type(pd.DataFrame)
# type

pd.DataFrame.from_dict # classmethod
# <bound method DataFrame.from_dict of <class 'pandas.core.frame.DataFrame'>>
class B:
  def aa(self):
    self.a = 1 
  
  def bb(self):
    self.b = 2   # 인스턴스 변수는 인스턴스가 사용한다 
    
b = B()
b.a
# AttributeError: 'B' object has no attribute 'a'

b.aa() # 함수 실행시점에서 인스턴스 변수에 접근 가능해진다 
b.a
# 1

vars(b)
# {'a': 1}
class test:
  a = 1 

  @classmethod
  def one(cls):
    cls.a = 1 
    print('cls')
  
  def two(self):
    self.b = 2
    
t = test()
t.a
# 1

t.b # 인스턴스 메소드가 실행된 시점 이후에 접근 가능하다 
# AttributeError: 'test' object has no attribute 'b'

t.two()
t.b
# 2

 

 

import tensorflow as tf

from tensorflow.keras.layers import Dense, Flatten, Conv2D
from tensorflow.keras import Model

mnist = tf.keras.datasets.mnist

(x_train, y_train), (x_test, y_test) = mnist.load_data() # 동적 생성 
x_train, x_test = x_train / 255.0, x_test / 255.0


class MyModel(Model):
  def __init__(self):     # callable 연산자 overloading / 역할은 기능을 초기화 한다 
    super(MyModel, self).__init__()
    self.conv1 = Conv2D(32, 3, activation='relu')
    self.flatten = Flatten()
    self.d1 = Dense(128, activation='relu')
    self.d2 = Dense(10)

  def call(self, x):
    x = self.conv1(x)
    x = self.flatten(x)
    x = self.d1(x)
    return self.d2(x)

# Create an instance of the model
model = MyModel()

※ 클래스안의 클래스 메소드와 인스턴스 메소드의 가장 큰 차이점은 클래스를 인스턴스화 했을 때 클래스 메소드 안에 있는 클래스 변수는 바로 접근 가능하지만, 인스턴스 메소드 안에 있는 인스턴스 변수는 인스턴스 변수가 실행된 시점 이후에 접근 가능해진다

class C:
  
  def __new__(cls):
    print('new')
  
  def __init__(self):
    print('init')
    
C()
# new

c = C()
# new

vars(c)
# TypeError: vars() argument must have __dict__ attribute
class C:
  
  def __new__(cls):    # 객체를 반환하는 것
    print('new')
    return super().__new__(cls)
  
  def __init__(self):  # 반환된 객체의 값을 지정해주는 것 / 초기화 용도로 사용 / return이 None이어야 한
    print('init')
    
c = C()  # 파이썬에서 인스턴스 생성은 두 단계로 나누어져 있다 
# new
# init

staticmethod

대형 프로젝트에서 주로 사용한다

class T:
  @staticmethod  # 기본적으로 python의 method는 정의 첫번째 인자 강제, 사용은 첫번째 인자 생략하는 것이 원칙이다 
  def st():      # staticmethos는 클래스 인스턴스와 상관 없이 존재한다 
    print('st')

T.st() # 체계적으로 구조화 할 수 있다 
# st

property, descriptor

descriptor 는 . 행동 결정 (get set del) => 정보 은닉에 사용

class D: # callable 관점에서

  def __init__(self, t):
    self._t = t

  @property # callable을 callable 하지 않게 변경
  def t(self):
    return self._t
  
  @t.setter
  def t(self, t):
    self._t = t

  def __getattribute__(self, x):
    if x == '_t':
      print('접근 안됨')

d = D(6)
d.t 
vars(d)
d._t
# 접근 안됨
class E:
  __a = 1 # mangling

E.__a
# AttributeError: type object 'E' has no attribute '__a'

E._E__a
# 1

 

 

Dispatch

Dispatch는 어떤 메소드를 호출할 것인가를 결정하여 그것을 실행하도록 유도해주는 과정을 말한다

 

python은 singledispatch만 지원한다

, multiple dispatch는 3rd party를 설치하면 구현 가능하다

 

파라미터가 한개면 single

파라미터가 두개 이상이면 multiple

 

len # type builtin_function or method / dispatch를 지원해주는 generic function

len([1,2,3]) -> list 관련 메소드로 dispatch

len((1,2,3)) -> tuple 관련 메소드로 dispatch # dispatch는 tensorflow predict에 숨어있는 개념이다

Single dispatch vs Multiple dispatch

# method overloading
# 다른 프로그래밍 언어에서는 parameter가 다르면 다른 함수로 간주한다 
# python에서는 method overloading을 지원하지 않는다 

def x():
    print('x')

def x(a):
    print('a')

Single dispatch

from functools import singledispatch

@singledispatch # 데코레이터 singledispatch함수에 x함수의 기능이 추가 되는 것이다 
def x(a):
    print(a)

@x.register(int)
def _(a):
    print('int', a)
    
@x.register(float)
def _(a):
    print('float', a)
    
# 데코레이터 관점에서 봤을 때 메소드 이름이 동일해도 서로 다르게 작동할 수 있다    
# 데이터 타입에 따라서 다른 메소드를 실행시킬수 있다

x('a')
# a

x(1)
# int 1

x(1.)
# float 1.0

Tensorflow predict (dispatch 예시)

import tensorflow as tf 
 
model = tf.keras.models.Sequential([
    tf.keras.layers.Dense(2, input_shape=(2,))
])
 
model.predict(tf.constant([[1,2]])) # numpy array type
# array([[-0.9186919, -1.4591577]], dtype=float32)


model(tf.constant([[1,2]])) # Tensor type
# <tf.Tensor: shape=(1, 2), dtype=float32, numpy=array([[-0.9186919, -1.4591577]], dtype=float32)>

Multiple dispatch

!pip install multipledispatch
from multipledispatch import dispatch

@dispatch(int, int)
def add(x, y):
    return x + y

@dispatch(object, object)
def add(x, y):
    return "%s + %s" % (x, y)

add(1,2)
# 3

add('1','2')
# '1 + 2'

※ 용어 정리

__함수이름__ 이러한 형태의 메소드: dundu, magic method, speicial method, 일반적으로 프로그래밍 언어에서는 protocol이라고 한다

 

from itertools import zip_longest
x = zip([1,2,3,4],[4,5,6])
list(x)
# [(1, 4), (2, 5), (3, 6)]

y = zip_longest([1,2,3,4],[4,5,6])
list(y)
# [(1, 4), (2, 5), (3, 6), (4, None)]

 

 

 

반응형

+ Recent posts