본문 바로가기

카테고리 없음

#파이썬 기초 6일차_2

728x90

1. 함수의 정의 및 호출

함수는 반복되는 로직을 하나의 기능으로 묶어 재사용할 수 있도록 구조화하여 모듈 내에서 사용할 수 있는 기능으로 정의하는 것입니다.

1.1 함수의 정의 및 호출

함수를 정의하는 이유: 함수를 미리 정의해 두면 프로그램이 로딩될 때 해당 함수의 실행 구조를 파이썬이 내부적으로 생성할 수 있습니다.

파라미터 전달: 함수에 파라미터는 함수 이름 뒤의 괄호 안에 콤마로 구분하여 전달됩니다.

 

  • 함수의 구현 방식
  • 정의문을 통한 함수 정의: 일반적인 방식으로 함수를 정의하고 사용할 수 있습니다.
  • 람다 표현식 (람다 함수, 익명 함수): 간단한 함수를 한 줄로 작성하여 사용할 수 있습니다.
def func(par1, par2):
    """ 이 함수는 아무 행동 하지 않음(구현 코드 블록) """
    pass

  print(func) # func을 통해 나온 리턴값을 변수처럼 생각해 출력
  print(type(func)) # func이 어떤 형태인지 보여줌
  print(func.__doc__) # 함수가 가지는 도큐먼트 부분을 보여줌

 

help(func)

Help on function func in module __main__:

main이 있는 친구는 실행할 수 있다.

 

함수의 호출

  • 함수가 정의되면 내부적으로 로딩 되면서 function class의 인스턴스로 만들어진다.
  • 함수의 매개변수(파라미터)는 함수의 지역변수로 사용된다.
def func(x,y):
  """ 이 함수는 x+y 결과를 출력 """
  return x+y

print(func(10,20))

30

 

함수 정의와 호출 순서

 

모듈 내에 함수가 정의된 경우

  • 모듈이 로딩될 때 함수도 로딩됨
  • 함수의 정의문에서 작성한 함수 이름을 key로, 함수의 인스턴스가 value로 들어감

파이썬은 인터프린터 언어임으로 위부터 실행됨

addx(7,3) # 구축 전 호출

def addx(x,y):
  return x+y

NameError: name 'addx' is not defined #어떤 구조인지 알기 전 호출되었음으로 실행 안됨

 

def addx(x,y):
  print("addx call")
  return x+y

 

전역으로 불렀을 때 주소

print(globals()["addx"])

<function addx at 0x7c09dec7fd00>

 

단순히 이름 불렀을 때 주소

전역으로 불렀을 때와 같은 주소를 가르킴

print(addx)

<function addx at 0x7c09dec7fd00>

print(globals()["addx"] is addx)

True

print(addx(50,60))

addx call

110

addx = globals()["addx"]
print(addx(20,30))

addx call

50

 

함수의 파라미터 자료형 제한하기

  • 파이썬은 자료형을 따로 지정하지 않고, 실행시 부여되는 시스템
  • 함수를 만들때는 특정 목적을 수행하기 위해 만들기에 목적에따라 자료형을 지정해야하는 경우가 생김
  • raise 는 예외를 강제로 발생시키는 것
def add(x,y):
    if not isinstance(x,int):
      raise ValueError("x is not integer")
    if not isinstance(y,int):
      raise ValueError("y is not integert")
    return x+y
add(11.1,10)

ValueError: x is not integer

add(10,11.1)

ValueError: y is not integert

add(10,10)

20

 

 함수 정의와 호출 순서

  1. 함수 정의
    • 모듈 내에 함수가 정의되면, 모듈이 로딩될 때 함수도 함께 로딩됨.
    • 함수의 정의문에서 함수 이름을 key로 하고, 함수의 인스턴스를 value로 갖는 딕셔너리 자료구조에 저장됨.
  2. 함수 조회
    • globals(): 모듈 내의 전역 네임스페이스를 조회하는 내장 함수.
    • 파이썬의 네임 스페이스는 딕셔너리 자료형으로 관리됨.
    • 따라서 함수의 이름을 문자열로 해서 globals() 결과를 조회할 수 있음.
    • 인스턴스의 레퍼런스(참조)가 동일한지 확인하려면 is 키워드로 비교함.
  3. 함수 호출
    • 함수의 이름으로 호출함.
    • 모듈의 전역 네임스페이스에서 이 함수의 이름을 확인.
    • 함수의 이름이 존재한다면 해당 함수의 인스턴스를 실행하여 결과를 반환.
    • 함수의 이름이 존재하지 않는다면 이름이 정의되지 않았다는 예외를 발생시킴.
  4. 함수 파라미터 자료형 제한하기
    • 제너릭(Generic) 함수: 어떤 자료형이라도 인자로 전달하여 호출할 수 있는 함수의 정의/처리 방식.
    • 파이썬의 함수는 기본적으로 제너릭 함수이므로, 매개변수에 특정 자료형으로 제한을 두려면 함수 내에서 별도의 로직으로 처리해야 함.

1.2 함수 호출 연산자 이해하기

 

  • 함수 호출 기본
    • 함수를 호출할 때는 함수 이름 뒤에 괄호를 사용하여 호출함.
    • 괄호 안에는 함수가 필요로 하는 인자들을 넣어 전달할 수 있음.
    • 예시: result = my_function(arg1, arg2)
  • 함수는 function 클래스의 인스턴스
    • 파이썬에서 함수는 기본적으로 function 클래스의 인스턴스임.
    • 따라서 함수 호출 연산자인 괄호는 이 클래스의 __call__ 메서드를 이용하여 처리됨.

 

 

함수 호출 연산자

  • 내장 함수 callable을 이용하여 함수가 호출이 가능한지 확인할 수 있음
  • 내장 함수 callalble은 객체 내의 스페셜 메서드인 __call__의 존재 여부를 확인해 있으면 True출력
  • 함수의 이름에 점 연산자를 이용하여 메서드 __call__을 출력하면 메서드의 레퍼런스를 확인

method-wrapper로 출력된 것은 이 메서드가 호출이 가능한 내부적인 메서드를 제공한다는 의미

 

def mul(x,y):
  return x*y
print(mul)

<function mul at 0x7c09decd3d90>

print(callable(mul))

True

print(mul(10,10))

100

print(mul.__call__)
print(mul.__call__(10,10))

<method-wrapper '__call__' of function object at 0x7c09decd3d90>

100

 

함수 객체의 바인딩 규칙

 

  • 클래스와 메서드 관리
    • 클래스는 모든 메서드를 관리하며, 메서드는 해당 클래스의 인스턴스 메서드로 정의됨.
  • 인스턴스와 메서드 호출
    • 클래스를 검색하여 인스턴스 메서드에 인스턴스 인자를 전달하면, 해당 메서드가 실행됨.
  • 함수 객체와 클래스
    • 함수도 파이썬에서는 function 클래스의 인스턴스로 취급됨.
    • 함수가 호출될 때는 내부적으로 __call__ 메서드가 호출되어 처리됨.
  • 함수 호출 바인딩 절차
    • 함수가 어떤 클래스의 인스턴스인지 확인하려면 __class__ 속성을 사용하여 해당 클래스 정보를 얻을 수 있음.
    • 예시: my_function.__class__
    • 함수 객체가 인스턴스이므로, 함수 객체의 __call__ 메서드를 점 연산자(.)를 사용하여 접근하면 이 메서드의 존재 여부를 확인할 수 있음.

 

def sub(x,y):
  return x-y
print(type(sub))
print(sub.__class__)

<class 'function'>

<class 'function'>

print(sub.__class__.__call__)
print(sub.__call__)

<slot wrapper '__call__' of 'function' objects>

<method-wrapper '__call__' of function object at 0x7c09decd2f80>

print(sub.__class__.__call__(sub,100,10))
print(sub.__call__(100,10))

90

90

 

1.3 함수 return문 처리

함수의 반환 처리

  • 함수는 기본적으로 반드시 return을 사용함
  • 결과를 반환할 필요가 없는 함수도 내부적으로 None을 반환함
  • 함수의 결과는 지역 네임스페이스를 통해 외부로 전달함
  • 지역 네임스페이스는 딕셔너리로 구성되므로 전달한 결과도 딕셔너리를 출력함

 

def str_prt(x):
  print(x)

a = str_prt("return문 없을 때")
print(a)

return문 없을 때

None

 

def str_prt(x):
  return x

a = str_prt("return문 있을 때")
print(a)

return문 있을 때

 

함수의 변환을 여러 개가 필요한 경우

  • 여러개의 값을 반환하려면 여러 개의 원소를 하나로 구성하는 자료형에 묶어서 반환함
  •  return a, b처럼 , 로 구분하는 것은 기본적으로 튜플을 생성하는 방법임. 즉 튜플로 반환함
def func_tuple(x,y,z):
  return x,y,z #튜플 형태로 가져옴

a = func_tuple(10,20,30) # 변수와 동일한 개수를 만들어주어, 대입연산자에 의거 할당됨
print(type(a),a)

<class 'tuple'> (10, 20, 30)

 

locals() 는 지역변수에 있는것을 전부 찾아옴 (파라미터는 지역변수로 전달되기 때문에 사용)

def func_dict(x,y,z):
  return locals()

a = func_dict(10,20,30)
print(type(a),a)

<class 'dict'> {'x': 10, 'y': 20, 'z': 30}

 

1.4 함수는 function 클래스의 인스턴스 객체

  • 파이썬은 모든 것을 객체로 관리
  • 함수는 function 클래스의 인스턴스
  • 함수가 만들어지면 기본 정보는 function 클래스의 속성에서 관리
  • 실행에 필요한 정보는 code 클래스 내에서 관리

 

함수의 기본 속성을 확인해 보기

  • 함수를 정의할 때 매개변수에 자료형과 초기값을 할당함
  • 매개변수 할당 시 * 에는 아무런 변수를 부여하지 않는 이유는 다음에 인자를 전달할 때에는 반드시 키워드 인자로 전달해야 함을 의미한다.
  • 반환값에 필요한 자료형을 선언하기 위해 매개변수를 정의한 다음 이후에 자료형을 정의, 반환값의 결과에 대한 자료형을 확정

 

자료형 고정

 

  • 파이썬은 모두 클래스로 구성되어있는데, 클래스를 통해 상송되면서 나아가는데, 클래스중 최상위 클래스를 object라 한다.
  • 오브젝트가 가장 보편적이면서 적은 메소드를 가지고 있다.(내려갈수록 메소드 늘어날 것이다)
import pprint

def div(x:int=100,*,y:int=100) -> float: #반환 자료형 float로 고정
  return x/y

s = set(dir(div))
o = set(dir(object))

pprint.pprint(s-o)

{'__annotations__',
 '__builtins__',
 '__call__',
 '__closure__',
 '__code__',
 '__defaults__',
 '__dict__',
 '__get__',
 '__globals__',
 '__kwdefaults__',
 '__module__',
 '__name__',
 '__qualname__'}

print(div())

1.0

print(div.__name__)
print(div.__qualname__)
print(div.__annotations__)
print(div.__defaults__)
print(div.__kwdefaults__)

div
div
{'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'float'>}
(100,)
{'y': 100}

print(div.__module__)
print(div) #div는 아래 주소를 가지는 함수이다.
print(div.__globals__['div'])

__main__

<function div at 0x7c09dec9ca60>

<function div at 0x7c09dec9ca60>

 

함수에 대한 사용자 객체속성 추가

  • 함수도 하나의 인스턴스 객체이므로 인스턴스 내부의 네임스페이스가 존재함
  • 따라서 함수에도 함수의 인스턴스를 추가하거나 삭제가 가능하다.
def func_state(x,y):
  func_state.count += 1
  return x+y
func_state.count = 0
print(func_state.__dict__)
print(func_state(10,10))
print(func_state(10,10))
print(func_state(10,10))
print(func_state.__dict__)

{'count': 0}
20
20
20
{'count': 3}

 

모듈 내의 함수에 대한 정보를 조회하기

def func_state(x, y):
    func_state.count += 1
    return x + y
print(func_state.__module__)
func_state.count = 0
print(func_state.__globals__['func_state'](10,10))
print(func_state.__dict__)

__main__
20
{'cout': 0, 'count': 1}

 

2. 함수의 변수 네임스페이스와 스코프 처리

스코프(scope)

  •   지역과 전역 등에 대한 처리규칙
  •     함수 내 지역 네임스페이스에 없는 것을 상위 모듈의 전역 네임스페이스를 검색해서 처리하는 것

 

  • 함수 정의 시 매개변수를 지정하면 함수 호출 시 변수와 값이 지역 네임스페이스에 할당됨
  •   함수 정의 시 매개변수에 초기값을 주는 경우
  •     이 초기값은 함수의 지역 네임스페이스에 만들어지지 않고 객체의 네임스페이스에서 별도의 속성에 들어감
  •     여러 번 함수가 호출되어도 단지 네임스페이스를 갱신하는 것이므로

        함수의 인스턴스 속성이 초기값 스페셜 속성을 변경하지 않음

  •     함수의 네임스페이스는 함수를 호출할 때마다 생성되므로 함수 실행이 종료되면 지역 네임스페이스는 사라진다.

 

  • 함수가 모듈과 전역 네임스페이스에 대한 정보를 가지고 다니는 이유
  •     함수가 항상 자신이 속한 모듈에서 처리되고
  •     전역 네임스페이스는 항상 자기가 속한 모듈을 사용하기 때문
  •     이렇게 해야 함수에 없는 것을 네임스페이스에서 찾아 호출하거나, 이곳에도 없으면 내장 네임스페이스를 검색하고, 거기에도 없으면 예외로 처리할 수 있음

 

2.1 함수 Namespace 및 scope 규칙

  • 파이썬 네임스페이스에는 전역, 지역, 그리고 제일 상위인 내장(builtions)이 있음
  • 지역과 전역의 범위
  •   모든 네임스페이스는 딕셔너리가 기본이므로 항상 키와 값을 쌍으로 처리함
  •   함수와 모듈에 정의된 변수는 네임스페이스의 키로 들어감

     이 키를 검색하여 값을 가져오고

     그 값이 어떤 자료형으로 저장되었나에 따라서 처리하는 연산이 달라짐

변수가 정의되면

  •     이 변수가 어느 영역의 네임스페이스에 저장되는지가 중요함
  •     그래야 변수의 참조에 대한 규칙에 따라 검색해서 발견되면 더 이상 상위로 가지 않고 그 값을 로직에서 처리할 수 있음
  •     변수의 참조 규칙은 Local > Global > Built-in 순서

        함수가 호출될 때 현재 위치부터 출발해서 네임스페이스를 처리함

        현재 위치에서 변수의 이름을 검색했는데 없으면 상위로 올라가고, 최상위까지 없다면 예외처리(스코프)

        함수의 전역 규약은 항상 자신이 속한 모듈(함수의 속성에 모듈과 전역 네임스페이스를 항상 가지고 있는 이유)

        파이썬 엔진에서 기본으로 제공하는 네임스페이스는 내장(Builtins)으로 변수 네임스페이스의 최상위가 됨

  • 모듈은 전역, 함수는 지역으로 인식

    주피터 노트북의 셀에서 %%writefile 파이썬 모듈명을 주고 그 밑으로 변수와 함수 등을 정의하면 하나의 모듈이 만들어짐

 

%%writefile glb.py

glb_var = 100

def add(x, y):
    print(locals())
    print("globals", add.__globals__['glb_var'])
    return x + y

print(add.__module__)
print(add.__globals__['add'])

Writing glb.py

import glb

glb

<function add at 0x7c09decd0a60>

print(glb.add(10,20))
print(globals()['glb'])

{'x': 10, 'y': 20}
globals 100
30
<module 'glb' from '/content/glb.py'>

 

함수 별로 지역 네임스페이스 처리

- 함수 호출 벗어나면 해당 내용 사라짐

def func_1(x,y):
  print(locals())

def func_2(x,y):
  print(locals())

func_1(100,100)
func_2(150,150)

{'x': 100, 'y': 100}

{'x': 150, 'y': 150}

 

2.2 함수 내부에서 전역 변수를 확정해서 처리 

 

자동으로 전역 변수 접근

def func_1(x,y):
  print(locals())
  print("func_1 z", z)

 
def func_2(x,y):
  print(locals())
  print("func_2 z", z)
 
z = 9999
func_1(10,20)
func_2(10,20)

{'x': 10, 'y': 20}
func_1 z 9999
{'x': 10, 'y': 20}
func_2 z 9999

print(globals()['z'])
print(globals()['func_1'])
print(globals()['func_2'])

9999

<function func_1 at 0x7c09debd8310>

<function func_2 at 0x7c09debdad40>

  • global 키워드로 글로벌 변수 명기하기
  • 같은 이름을 쓴다해도 글로벌에 등록된거랑 지역변수에 등록된거랑 차이가 있다.
glb_var = 8888

def add(y):
  global glb_var
  glb_var = y
  return glb_var

print(glb_var)
print(add(10))
print(glb_var)

8888

10

10

glb_var = 8888

def add(y):
  #global glb_var
  glb_var = y
  return glb_var

print(glb_var)
print(add(10))
print(glb_var)

8888

10

8888

 

2.3 함수에서 객체 네임스페이스 접근하기

    타 클래스의 속성 참조

import pprint

class A:
  name = "A"
  def __init__(self):
    self.name = "A class instance"

 

아무 파라미터도 받지 않고 클래스 A를 받는것( A.name 여기서 A는 클래스, 클래스를 통해서 이동- 전역 네임스페이스)

def class_func():
  return A.name

print(class_func())
pprint.pprint(A.__dict__)

A
mappingproxy({'__dict__': <attribute '__dict__' of 'A' objects>,
              '__doc__': None,
              '__init__': <function A.__init__ at 0x7c09dec9ef80>,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'A' objects>,
              'name': 'A'})

i = A()
print(i.name)

A class instance

 

a.name는 변수 a 전달하려고 한 것 (인스턴스를 통해서 이동 -인스턴스에 있는거는 로컬 네임스페이스)

def instance_func(a):
  return a.name
print(instance_func(i))

A class instance

pprint.pprint(i.__dict__)

{'name': 'A class instance'}

 

3. 람다 함수

3.1 람다 함수의 정의

함수 정의문과 람다 함수의 변수 할당 비교

def add(x,y):
  return x+y
print(add(10,15))

25위 식을 lambda함수로 바꾸면 아래와 같다

a = lambda x,y: x+y
print(a(10,15))

25

a라는 변수를 통해 람다 함수에 접근하게 된다.(위 코드에서 보면)

 

람다 함수도 function class 객체 여부 확인

람다도 funchtion에 대부분 값들을 다 가지고 있다.

lam = set(dir(lambda x,y: x+y))
print(type(lambda x,y : x+y))
 
obj = set(dir(object))

for i in (lam-obj):
  print(i)

<class 'function'>
__globals__
__call__
__qualname__
__dict__
__module__
__builtins__
__annotations__
__name__
__defaults__
__code__
__kwdefaults__
__closure__
__get__

 

람다함수와

a = lambda x,y : x+y
print(a)
print(a.__name__)
print(a.__qualname__)

<function <lambda> at 0x7c09debd9870>
<lambda>
<lambda>

일반 함수와의 비교

print(add)
print(add.__name__)
print(add.__qualname__)

<function add at 0x7c09debd9e10>
add
add

 

print(a.__annotations__)
print(a.__defaults__)
print(a.__kwdefaults__)

{}

None

None

 

a = lambda x =100, * , y=100 : x+y
print(a.__annotations__)
print(a.__defaults__)
print(a.__kwdefaults__)

{}
(100,)
{'y': 100}

 

위 내용들을 보아 람다는 함수와 거희 동일한 성질을 띈다.

728x90