함수와 클래스(공학자를 위한 Python)
공학자를 위한 Python 책을 참고하여 작성하였습니다.
- 책 제목: 공학자를 위한 Python
- 지은이: 조정래
- url: https://wikidocs.net/book/1704
1. 함수
Python에서 함수는 def function(…): 형태로 정의한다.
def add(a, b):
return a + b
def printSomething(a, b, c):
print(a, b, c)
def printLogo():
print('logo text ...')
c = add(1, 2)
print(c)
printSomething(1, 2, 3)
printLogo()
3
1 2 3
logo text ...
1-1. 함수의 리턴값
여러 값을 리턴할 때 튜플로 반환한다. 값을 받을 때는 한 개의 변수로 받을 수도 있고 unpack 기능을 이용해 리턴값으로 튜플을 모아도 된다.
def computeProps(n, p, q):
...
return (a, b, c)
r = computeProps(...)
(a, b, c) = computeProps(...) # unpack 활용
a, b, c = computeProps(...) # 위와 동일
1-2. Immutable과 Mutable 타입의 인자
Immutable 자료형(숫자, 문자열 등 기본 자료형과 쓰기가 불가능한 컨테이너인 tuple)은 함수 내에서 값을 변경하더라도 호출 측에서 값이 변경되지 않는다. 즉 call by value로 인자가 전달된다.
- immutable: 변경할 수 없는, 불변의
def foo(a, b):
a = 100
b = 200
a = 1
b = 2
foo(a, b)
print(a, b) # 1, 2 출력
1 2
Mutable 자료형(쓰기가 가능한 컨테이너인 list, dictionary, ndarray 등)이 함수의 인자로 전달되는 경우 함수 안에서 값이 변경될 수 있다는 점에 주의해야 한다. C / C++ 관점에서 보면 call by reference로 인자를 전달하는 것이다.
- mutable: 변할 수 있는, 잘 변하는
def foo(x):
x.append(100)
x = [1, 2, 3]
foo(x)
print(x) # 1, 2, 3, 100
[1, 2, 3, 100]
위에서 인자로 넘겨진 x는 mutable 자료형인 리스트이므로, 함수 내에서 변경하는 경우 함수 호출 이후에도 값이 바뀌게 된다.
인자로 mutable 자료형을 전달했을 때 함수 내에서 값이 바뀌는지 여부를 주의해야 하지만, 다른 한편으로는 리턴값이 아닌 인자를 받아오도록 설계하는 데 사용할 수도 있다.
import numpy as np
def myfunc(x, grad):
f = x[0] * x[0] + x[1] * x[1]
grad[0] = 2 * x[0]
grad[1] = 2 * x[1]
return f
x = np.array([0.5, 0.1])
grad = np.zeros(2) # 길이가 2인 0 벡터 생성
f = myfunc(x, grad)
print(f)
print(grad)
0.26
[1. 0.2]
np.zeros(2)는 주어진 크기의 모든 요소가 0으로 초기화된 배열을 생성합니다. 이 경우에는 길이가 2인 1차원 배열이 생성됩니다.
1-3. 디폴트 인자의 지정
def computeIgIcr(n, b, h, Ast, dt, Asc, dc, opt = 'exact'):
...
Ig = computeIg(n, b, h, Ast, dt, Asc, dt)
Ig = computeIg(n, b, h, Ast, dt, Asc, dt, 'appr')
1-4. *arg와 **kwargs
인자의 개수가 정해지지 않을 때 사용하는 것이 *arg와 **kwargs이다.
def add(*args):
r = 0
for v in args:
r += v
return r
r = add(1, 2, 3)
print(r)
6
*args에서 *은 여러개의 인자를 튜플로 묶어 args 변수로 함수로 전달하게 된다. 위에서 arg = (1, 2, 3) 형태로 전달하게 된다. 일반 인자와도 같이 사용할 수 있는데 항상 뒤에 와야 한다. def foo(x, *args) 형태이고, foo(1, 2, 3, 4, 5)로 호출했다면 x = 1, args = (2, 3, 4, 5)로 전달하는 식이다.
비슷한 것으로 **kwargs가 있다. 이는 keyword arguments로 이해할 수 있는데 keyword1 = value1, keyword2 = value2, … 형태를 dictionary로 만들어 준다.
def myfunc(**kwargs):
print(kwargs)
for key, value in kwargs.items():
print(key, '=', value)
myfunc(a = 1, b = 2, c = 3)
{'a': 1, 'b': 2, 'c': 3}
a = 1
b = 2
c = 3
*args와 **kwargs는 일반 인자와 함께 사용될 수 있으며, 순서는 항상 다음과 같다.
def some_func(fargs, *args, **kwargs):
pass
1-5. 변수의 범위(scope)
함수 내에 사용하는 변수는 local scope를 갖는다.
1-6. 함수 내 함수
Python은 C/C++과 달리 함수 내에서 함수를 정의해서 사용할 수 있다. 함수 내 함수는 자신을 둘러싼 함수의 변수를 마치 전역변수처럼 사용할 수 있기 때문에 사용하기에 따라 매우 편리하게 사용가능하다.
def outside():
outsideList = [1, 2]
def nested():
outsideList.append(3)
nested()
print(outsideList)
2. 클래스
Python의 클래스는 class 키워드를 통해 선언할 수 있다. 다음은 가장 간단하게 정의한 예이다.
class Simple:
pass
위와 같이 정의한 후 객체를 생성한 후 멤버변수를 추가할 수도 있다.
a = Simple()
a.name = 'Jane'
a.phone = '123-456-7890'
# 일반적인 사용법
class Account:
numOfAccount = 0
def __init__(self, name):
self.name = name;
self.balances = 0
Account.numOfAccount += 1
def withdraw(self, value):
self.balances -= value
return self.balances
def deposit(self, value):
self.balances += value
return self.balances
def __del__(self):
Account.numOfAccount -= 0
a1 = Account('John')
a1.deposit(10)
10
a1.withdraw(2)
8
print(a1.balances)
8
print('no of Account : ', Account.numOfAccount)
no of Account : 1
Account 클래스는 클래스 단위로 정의한 변수(클래스 변수, C의 static variable과 동일)인 numOfAccount, 인스턴스 변수(C의 멤버변수) name, balances, 생성자 init(), 소멸자 del(), 일반 메서드 withdraw(), deposit()등을 정의하고 있다. self는 C 클래스 정의 시 생략되는 this 포인터와 같은 역할을 한다. 또한 Python에서는 기본적으로 멤버와 메서드가 public 속성을 지닌다. 만약 private으로 하고 싶을 때 두 개의 밑줄 __ 로 시작하도록 이름을 정의하면 된다. 메서드에 self 인자가 없는 경우 C의 static member와 동일하다. 이 함수에서는 클래스 멤버 변수를 조작하거나 단순히 namespace를 사용하는 함수처럼 사용해야 한다.
class Account:
numOfAccount = 0
...
def makeZero(number):
Account.numOfAccount = number
Python의 클래스에서도 상속(inheritance)과 가상함수 등을 지원한다.
class Element:
def __init__(self, id):
self.id = id
self.nodeIds = []
def computeStiffness(self):
print('Element::computeStiffness')
def printElement(self):
print('id : %d' % self.id)
class Q4Element(Element):
def __init__(self, id, nodeIds):
super().__init__(id) # or Element.__init__(self, id)
self.nodeIds = nodeIds
def computeStiffness(self):
print('Q4Element::computeStiffness')
e = Q4Element(1, [1, 2, 3])
e.printElement()
id : 1
e.computeStiffness()
Element::computeStiffness
위에서 부모 클래스의 생성자인 init()를 호출하는 방법은 두 가지다. super().init(id) 등과 같이 self를 사용하는 방법과 Element.init(self, id)와 같이 클래스 이름을 사용하고 메서드에 self를 사용하는 방법이 있다. 이런 방법은 일반 메서드에서도 성립한다. 또한 이름이 같은 메서드가 가상함수가 된다.
3. 정적함수를 이용한 다중 생성
Python 클래스는 keyword 입력을 허용하기 때문에 다중 생성자를 쓰기 쉽지 않다. 이를 위해서는 variable 입력을 받든지, 정적함수를 이용하는 방법을 사용할 수 있다.
# 방법 1
class A:
def __init__(self, *arg, **karg):
... arg와 karg를 분석
# 정적함수
class Rectangle:
def __init__(self):
self.a = None
self.b = None
def fromWithHeight(a, b):
r = A()
r.a = a
r.b = b
return r
def fromArea(a, area):
r = Rectangle()
r.a = a
r.b = area / a
return r
댓글남기기