-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
224 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
--- | ||
layout: single | ||
title: "Deep Dive super() in python" | ||
date: 2023-01-06 11:56:13 +0900 | ||
categories: python language | ||
toc: true | ||
toc_sticky: true | ||
tags: python | ||
comments: true | ||
enable_copy_code_button: true | ||
--- | ||
|
||
|
||
어느 언어에서든 super 는 상속하는 parent class 를 가져오는 keyword 다. single inheritance 인 경우밖에 없다면 이 문서를 작성할 필요도 없었을텐데, 복잡한 인자를 사용한 경우를 만났는지 링크를 남겨두었고, 미래의 내가 문서를 작성하고 있다. | ||
|
||
## bound/unbound method | ||
|
||
- bound method | ||
- 어떤 클래스에 속해있는 메소드 | ||
- unbound method | ||
- class instance 를 인자로 받지 않는 메소드 | ||
- 파이선 3으로 올라오면서 사라진 개념임 | ||
- instance 를 받지 않는 것은 static_method 로서 만들수는 있음. | ||
|
||
```python | ||
# adding a method to existing object | ||
>>> def bar(self): | ||
... print ("bar") | ||
... | ||
>>> | ||
>>> | ||
>>> bar | ||
<function bar at 0x1033828e0> | ||
>>> class A: | ||
... pass | ||
... | ||
>>> | ||
>>> A.bar=bar | ||
>>> A.bar | ||
<function bar at 0x1033828e0> | ||
>>> A.bar= bar.__get__(A) | ||
>>> | ||
>>> A.x | ||
<bound method bar of <class '__main__.A'>> | ||
|
||
``` | ||
|
||
## Descriptor | ||
|
||
https://docs.python.org/3.7/howto/descriptor.html | ||
|
||
## Multiple Inheritance | ||
|
||
`Cube` → `Square` → `Rectangle` 의 상속 관계에서 Cube 에서 Rectangle 의 area 가 아닌 Square 의 area method 를 사용하고 싶은 경우 아래와 같이 사용할 수 있다. | ||
|
||
```python | ||
class Cube(Square): | ||
def surface_area(self): | ||
face_area = super(Square, self).area() | ||
return face_area * 6 | ||
``` | ||
|
||
보통 multiple inheritance 라고 하면 이런 형태의 연쇄 상속이 아니고 아래와 같은 형태일 것이다. | ||
|
||
``` | ||
superclass1 superclass2 | ||
\ / | ||
subclass | ||
``` | ||
|
||
모든 superclass 가 .area 메소드를 정의하고 있다면 python 은 super().area() 를 호출했을 때 어떤 것을 가져올까? | ||
|
||
`method resolution order` 가 결정해줄 것이다. | ||
|
||
## Method Resolution Order | ||
|
||
모든 클래스는 `.__mro__` attribute 을 지니고 있다. 이는 super() 를 했을 때 어떤 class 를 우선적으로 탐색해서 가져올지 알려준다. | ||
|
||
```python | ||
class RightPyramid(Triangle, Square): | ||
def __init__(self, base, slant_height): | ||
self.base = base | ||
self.slant_height = slant_height | ||
|
||
def area(self): | ||
base_area = super().area() | ||
perimeter = super().perimeter() | ||
return 0.5 * perimeter * self.slant_height + base_area | ||
``` | ||
|
||
위의 코드에서 RightPyramid 의 상속 구문에서 Triangle 이 더 앞에 있기 때문에 mro 는 우선적으로 Triangle 을 찾게 될 것이다. | ||
또, Square 가 Rectangle 을 상속했기 때문에 Square 를 우선적으로 볼 것이다. | ||
|
||
```python | ||
>>> RightPyramid.__mro__ | ||
(<class '__main__.RightPyramid'>, <class '__main__.Triangle'>, | ||
<class '__main__.Square'>, <class '__main__.Rectangle'>, | ||
<class 'object'>) | ||
|
||
``` | ||
|
||
그러니... mro 에 의존할 것이 아니고 super 를 사용할 때 사용할 class 를 명시하는 게 권장될 것 같다. | ||
|
||
## 참고 | ||
|
||
- [bound/unbound method](https://www.geeksforgeeks.org/bound-unbound-and-static-methods-in-python/) | ||
- [deep dive super](https://realpython.com/python-super/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,118 @@ | ||
--- | ||
layout: single | ||
title: "Singleton approach in python" | ||
date: 2023-01-06 11:56:13 +0900 | ||
categories: python language | ||
toc: true | ||
toc_sticky: true | ||
tags: python | ||
comments: true | ||
enable_copy_code_button: true | ||
|
||
--- | ||
|
||
|
||
c++ 의 경우에 보통 static variable 을 이용해서 구현하는 것이 일반적인데 반해 python 에서 singleton 을 구현하는 방법은 딱히 idiomatic 하게 정해져있지 않다. | ||
static 변수나 전역 변수를 만드는 것은 그렇게 유쾌한 경험을 제공해주지 않는다. | ||
결론부터 말하자면 참고하는 문서에서는 Metaclass 방식을 가장 선호하는 방법으로 추천한다. 테스트하기에 좋기 때문. | ||
|
||
## 구현 후보 | ||
|
||
### Metaclass | ||
|
||
생성시에 class 를 변경할 수 있는 특별한 방법을 제공. 어떤 class 로 instance 를 생성한 것이 있다면 instance 를 또 생성하지 않게 하는 metaclass 를 만들 수 있음 . | ||
|
||
```python | ||
class Singleton(type): | ||
_instances = {} | ||
|
||
def __call__(cls, *args, **kwargs): | ||
if cls not in cls._instances: | ||
cls._instances[cls] = super(type(cls), cls).__call__(*args, **kwargs) | ||
return cls._instances[cls] | ||
|
||
class _A(): | ||
def __init__(self, name): | ||
self.name = name | ||
|
||
|
||
class A(_A, metaclass=Singleton): | ||
pass | ||
``` | ||
|
||
### Decorator | ||
|
||
decorator 를 이용해서도 구현할 수 있음. | ||
```python | ||
|
||
def singleton(class_): | ||
instances = {} | ||
|
||
def get_instance(*args, **kwargs): | ||
if class_ not in instances: | ||
instances[class_] = class_(*args, **kwargs) | ||
return instances[class_] | ||
|
||
return get_instance | ||
|
||
|
||
@singleton | ||
class A: | ||
def __init__(self, name): | ||
self.name = name | ||
|
||
|
||
|
||
``` | ||
|
||
### Classic singleton ( allocation) | ||
|
||
원래 클래스가 제공하는 기능인 `__new__` 메소드를 이용. | ||
|
||
```python | ||
|
||
|
||
class A: | ||
initialized = False | ||
name = None | ||
|
||
def __init__(self, name): | ||
if not self.initialized: | ||
self.name = name | ||
self.initialized = True | ||
|
||
def __new__(cls, url, *args, **kwargs): | ||
if not cls._instance: | ||
cls._instance = super(Database, cls).__new__(cls, *args, **kwargs) | ||
return cls._instance | ||
|
||
|
||
|
||
|
||
``` | ||
|
||
## 장단점 비교 | ||
|
||
* Metaclass | ||
* 장점 | ||
* test 를 할 때 singleton 기능없이 순순하게 해당 class 가 잘 동작하는지 구분해서 확인해볼 수 있음. | ||
* 예를 들면 _A 에 대한 객체를 하나 생성해서 기능 테스트 가능 | ||
* 단점 | ||
* decorator 에 비해 `metaclass=` 의 인자를 넣어줘야한다는 것이 귀찮음 | ||
* Decorator | ||
* 장점 | ||
* 여러 클래스에 쉽게 적용하기 좋음 | ||
* 코드 양이 가장 적음 | ||
* 단점 | ||
* test 시에 singleton 특성을 분리하기 어려움. | ||
* Classic | ||
* 장점 | ||
* 초창기 python 의 문법만 알고있어도 구현을 이해하는데 문제 없음 | ||
* 단점 | ||
* 코드가 지저분함 | ||
* 여러 클래스에 적용하기 어려움 | ||
|
||
## 참고 | ||
|
||
https://itnext.io/deciding-the-best-singleton-approach-in-python-65c61e90cdc4 | ||
https://realpython.com/python-super/ | ||
|