```python

*args

  # 가변인수

**kwargs

  # 가변 키워드 인수. key=value 형태의 가변 길이 인수를 받을 때. dict 형태로 넘겨받게 된다.

```


키워드 인수를 반드시 key=value 형식으로만 받고 싶을 때

```python
def test(arg, key=None):
    pass

test(1, 2)
```

positional arg 다음에 이어서 그냥 인자를 넘기면 알아서 위치대로 키워드 인자로 들어간다. 

반드시 ``py key=3``처럼 지정해주어야만 동작하도록 하고 싶은 경우, ``py *``을 적는다. 라이브러리 코드 보면 저런게 꽤 많다.

```python

def test(arg, *, key=None):

```


return

``python return (a, b)``
C 였다면 여러 (타입의) 값을 외부에 전달하기 위해 구조체를 사용하거나, 포인터를 이용했을 것.
리스트, 투플에 들어갈 타입을 한정하지 않기 때문에 투플을 만들어서 바로 리턴할 수 있음.

``python return x > 0``
return에 조건을 적을 수 있다. 조건에 맞게 True, False를 반환한다.

* return에 조건식을 적지 못하는 다른 언어였다면 삼항 연산자를 이용하거나, if로 처리했을 것이다.
```python
return x > 0 ? true : false
또는
if x > 0
return true
else
return false
```

``python global``
붙이면 외부 변수에 접근할 수 있지만 역시 안쓰는게 좋다.


Class

클래스는 다음과 같이 정의한다

```python

class 클래스명[(상속 클래스명)]:

```

상속하는 경우 메소드 오버라이딩할 수 있다.


instance method, class method / self, cls

```python

class Tst:

    def funcTst(self, a, b):


    @classmethod

    def classTst(cls, a, b):

```

객체에서 메서드(클래스 함수)를 호출할 때 객체 자기 자신(``python self)``이 첫 번째 인자로 넘어간다.

그래서 ``python ob.funcTst(3, 2)`` 이렇게 두 개의 인자를 넘겨도 실제로는 ``python self, 3, 2`` 세 개의 인자가 넘어가게 된다.

그래서 메서드를 정의할 때 관례적으로 첫번째 인자는 ``python self``를 사용한다. 자바의 ``java this``같은거라고 생각하면 된다.

class method로 정의된 method의 첫번째 인자로는 ``python self``가 아니라 ``python cls``를 사용한다.


@classmethod

``python @classmethod``를 붙이면 class method가 된다.

class method는 객체 정의 안하고 바로 호출한다.

```python 

Tst.classTst(a, b)

```


@staticmethod

이 것도 있는데 잘 안쓴다.

클래스 변수, 객체 변수(인스턴스 변수)

class 바로 하위에 선언된 변수는 클래스 변수다. 자주 쓰지는 않는다.

java의 static 변수와 비슷하다고 생각하면 된다. 

한 클래스에 하나만 만들어져서 어떤 인스턴스에서 수정하든 하나 수정하면 다 수정된다. 


클래스 내에서 클래스 변수에 접근하기 위해서는 ``python 클래스명.`` 을 사용한다.

클래스 내에서 객체 변수에 선언/접근하기 위해서는 ``python self.`` 을 사용한다.

그냥 var를 사용하면 메서드에 var라는 변수가 만들어지니 주의.

```python

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``는 ``python ob.var / Tst.var`` 둘 다 가능하지만, 구분을 위해 ``python Tst.var``로 쓰는 것이 좋다.

`` name``은 ``python ob.name``만 가능하다.

``python __init__``을 호출하면서 객체 변수 `` name``이 생성되기 때문에, 이는 객체 `` ob``에 종속된다.


getter / setter

``python property(get_var, set_var)`` 또는``python @property / @var.setter``를 이용해 
getter / setter를 설정할 수 있으나 굳이 이렇게 해야 하는 경우는 잘 없다.

Special method names

__slots__

클래스의 인스턴스는 기본적으로 attribute storage를 위한 딕셔너리를 가지고 있다.(여기서 attribute는 instance attribute를 말하는 듯) 이로인해 인스턴스 변수가 별로 없는 경우 객체의 공간을 낭비하게 된다.
* attribute = 변수

__slots__를 override해서 인스턴스 변수를 리스트나 투플로 지정하고 나서 사용하면 메모리 낭비를 줄이고 속도를 향상시킬 수 있다.
```python
__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__

정의하면 ``python print(obj)``했을 때 이 메소드에 지정된 형식 대로 출력된다.


연산자 오버로딩

``python __add__(self, other)``

``python __eq__(self, other)``

등등을 사용하면 객체끼리 연산자를 사용할 수 있으며 

연산자를 사용하면 앞쪽 함수의 ``python __add__(self, other)`` 등이 호출된다.