(python) 함수, 클래스, 객체, \*args, \*\*kwargs
1
2
3
4
\*args
## 가변인수
\*\*kwargs
## 가변 키워드 인수. key=value 형태의 가변 길이 인수를 받을 때. dict 형태로 넘겨받게 된다.
키워드 인수를 반드시 key=value 형식으로만 받고 싶을 때
1
2
3
def test(arg, key=None):
pass
test(1, 2)
positional arg 다음에 이어서 그냥 인자를 넘기면 알아서 위치대로 키워드 인자로 들어간다. 반드시 key=3
처럼 지정해주어야만 동작하도록 하고 싶은 경우, \*
을 적는다. 라이브러리 코드 보면 저런게 꽤 많다.
1
def test(arg, \*, key=None):
return
return (a, b)
C 였다면 여러 (타입의) 값을 외부에 전달하기 위해 구조체를 사용하거나, 포인터를 이용했을 것. 리스트, 투플에 들어갈 타입을 한정하지 않기 때문에 투플을 만들어서 바로 리턴할 수 있음. return x > 0
return에 조건을 적을 수 있다. 조건에 맞게 True, False를 반환한다. * return에 조건식을 적지 못하는 다른 언어였다면 삼항 연산자를 이용하거나, if로 처리했을 것이다.
1
2
3
4
5
6
return x > 0 ? true : false
또는
if x > 0
return true
else
return false
global
붙이면 외부 변수에 접근할 수 있지만 역시 안쓰는게 좋다.
Class
클래스는 다음과 같이 정의한다
1
class 클래스명[(상속 클래스명)]:
상속하는 경우 메소드 오버라이딩할 수 있다.
instance method, class method / self, cls
1
2
3
4
5
6
7
class Tst:
def funcTst(self, a, b):
@classmethod
def classTst(cls, a, b):
객체에서 메서드(클래스 함수)를 호출할 때 객체 자기 자신(self)
이 첫 번째 인자로 넘어간다. 그래서 ob.funcTst(3, 2)
이렇게 두 개의 인자를 넘겨도 실제로는 python self, 3, 2
세 개의 인자가 넘어가게 된다. 그래서 메서드를 정의할 때 관례적으로 첫번째 인자는 self
를 사용한다. 자바의 this
같은거라고 생각하면 된다. class method로 정의된 method의 첫번째 인자로는 self
가 아니라 cls
를 사용한다.
@classmethod
@classmethod
를 붙이면 class method가 된다. class method는 객체 정의 안하고 바로 호출한다.
1
Tst.classTst(a, b)
@staticmethod
이 것도 있는데 잘 안쓴다.
클래스 변수, 객체 변수(인스턴스 변수)
class 바로 하위에 선언된 변수는 클래스 변수다. 자주 쓰지는 않는다.
java의 static 변수와 비슷 하다고 생각하면 된다. 한 클래스에 하나만 만들어져서 어떤 인스턴스에서 수정하든 하나 수정하면 다 수정된다.
클래스 내에서 클래스 변수 에 접근하기 위해서는 클래스명.
을 사용한다.
클래스 내에서 객체 변수 에 선언/접근하기 위해서는 self.
을 사용한다. 그냥 var를 사용하면 메서드에 var라는 변수가 만들어지니 주의.
1
2
3
4
5
6
7
8
9
10
class Tst:
var = "Tst, string var"
def \_\_init\_\_(self, name):
self.name = name
Tst.var = 33
def funcTst(self, a, b):
result = a + b
print("Tst," + self.name + "funcTst : "+str(result))
def \_\_del\_\_(self):
pass # 소멸자
클래스 변수, 객체 변수 둘 다 외부에서 접근 가능하다.
클래스 변수 var
는 ob.var / Tst.var
둘 다 가능하지만, 구분을 위해 python Tst.var
로 쓰는 것이 좋다. name
은 ob.name
만 가능하다. \_\_init\__
을 호출하면서 객체 변수 name
이 생성되기 때문에, 이는 객체 ob
에 종속된다.
getter / setter
property(get\_var, set_var)
또는python @property / @var.setter
를 이용해 getter / setter를 설정할 수 있으나 굳이 이렇게 해야 하는 경우는 잘 없다.
Special method names
__slots__
클래스의 인스턴스는 기본적으로 attribute storage를 위한 딕셔너리를 가지고 있다.(여기서 attribute는 instance attribute를 말하는 듯) 이로인해 인스턴스 변수가 별로 없는 경우 객체의 공간을 낭비하게 된다. * attribute = 변수
__slots__를 override해서 인스턴스 변수를 리스트나 투플로 지정 하고 나서 사용하면 메모리 낭비를 줄이고 속도를 향상시킬 수 있다.
1
\_\_slots\_\_ = ('url', 'file')
__slots__를 지정하는 경우 그 인스턴스에 대해 __dict__가 자동으로 생성되는 것을 막아주고 지정한 변수를 위한 공간만 할당하기 때문에 메모리가 절약되는 것이라고 한다.
Notes on using__slots__
- __dict__가 없기 때문에 인스턴스는 __slots__에 명시되지 않은 새로운 변수를 할당할 수 없다. 할당하는 경우 AttributeError가 발생한다.
- 만약 새로운 변수를 동적으로 할당해야 하는 상황이라면 __slots__에 지정된 변수 목록에 __dict__를 추가하는 방식으로 사용 가능하다.
- __slots__가 없는 class에서 상속받는 경우 __dict__가 자동으로 상속되기 때문에 subclass에서 __slots__구현은 무의미하다.
- 반대로 __slots__가 있는 class에서 상속받더라도 subclass에서 자동으로 __dict__가 생성되는 것을 막으려면 __slots__를 구현해주어야 한다.
- https://docs.python.org/3/reference/datamodel.html#slots
__slots__을 지정하지 않아 __dict__가 있는 경우, 클래스에 없는 변수도 객체에 생성할 수 있다. 오타 조심.
__repr__
정의하면 print(obj)
했을 때 이 메소드에 지정된 형식 대로 출력된다.
연산자 오버로딩
\_\_add\__(self, other)
\_\_eq\__(self, other)
등등을 사용하면 객체끼리 연산자를 사용할 수 있으며 연산자를 사용하면 앞쪽 함수의 \_\_add\__(self, other)
등이 호출된다.