728x90
반응형

Python

Python에서는 모든 것이 객체다

함수도 객체 파일도 객체 가능한 모든 것들이 객체

Object (type)의 내부 정보를 알고 싶을 때

1. type 2. dir 3. id 4. vars

이름/식별자를 불렀을 때 내부적으로 작동하는 것들

1. REPR 2. STR

class A:
  def __repr__(self):
    return 'REPR'

  def __str__(self):
    return 'STR'
a = A()
a # 이름을 부르면 __repr__가 호출된다 
# REPR

print(a) # 이름을 print하면 __str__이 호출된다 / escape를 처리해준다 
# STR

※ self를 붙이는 이유

class A: 
  def __init__(this):  # self 대신 다른 것 사용 가능 그러나 pep8에서 self 사용 권장 
    print('a') 
  def __call__(self):  # 객체 지향에서 self는 인스턴스(object)를 뜻한다  
    print('call')
  def aa(self):
    print('aa')

# python 내부에서 c기반으로 작동하기 때문에 (c는 method 개념이 없다) 이와 같이 만들어 졌다 
a = A()
A.aa(a) # 어떤 인스턴스에 대해서 사용할 것인가 인자가 필요 / version 1 / 주체가 class A 
a.aa(a) # 인스턴스가 주체이고 싶을 때 / argument가 중복되기 때문에 a 생략하게 만들었다 
a.aa()
 import itertools 
 dir(itertools) #__package__ / dir은 객체의 모든 attribute를 조회 함수이다 
 ['__doc__',
 '__loader__',
 '__name__',
 '__package__',
 '__spec__',
 '_grouper',
 '_tee',
 '_tee_dataobject',
 'accumulate',
 'chain',
 'combinations',
 'combinations_with_replacement',
 'compress',
 'count',
 'cycle',
 'dropwhile',
 'filterfalse',
 'groupby',
 'islice',
 'permutations',
 'product',
 'repeat',
 'starmap',
 'takewhile',
 'tee',
 'zip_longest']
type(itertools) # module이라는 데이터 타입 
# module

id(itertools)
# 140663255600208

dir(int) # 클래스도 객체 이다 
['__abs__',
 '__add__',
 '__and__',
 '__bool__',
 '__ceil__',
 '__class__',
 '__delattr__',
 '__dir__',
 '__divmod__',
 '__doc__',
 '__eq__',
 '__float__',
 '__floor__',
 '__floordiv__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getnewargs__',
 '__gt__',
 '__hash__',
 '__index__',
 '__init__',
 '__init_subclass__',
 '__int__',
 '__invert__',
 '__le__',
 '__lshift__',
 '__lt__',
 '__mod__',
 '__mul__',
 '__ne__',
 '__neg__',
 '__new__',
 '__or__',
 '__pos__',
 '__pow__',
 '__radd__',
 '__rand__',
 '__rdivmod__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__rfloordiv__',
 '__rlshift__',
 '__rmod__',
 '__rmul__',
 '__ror__',
 '__round__',
 '__rpow__',
 '__rrshift__',
 '__rshift__',
 '__rsub__',
 '__rtruediv__',
 '__rxor__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__sub__',
 '__subclasshook__',
 '__truediv__',
 '__trunc__',
 '__xor__',
 'bit_length',
 'conjugate',
 'denominator',
 'from_bytes',
 'imag',
 'numerator',
 'real',
 'to_bytes']

객체 지향

[객체지향의 4가지 특성]

1. 캡슐화

- 실제 구현내용을 감추는

- 재활용 가능

- 파이썬에서는 기본적으로 외부에서 클래스 접근 가능

- descriptor로 클래스 접근을 막을 수도 있다

2. 추상화

- 필요로 하는 속성이나 행동을 추출하는 작업

- 구체적인것과 추상적인 것을 분리시킨다

- 특징을 뽑아 객체로 구현

3. 상속

- 추상화의 반대

- 추상화한 클래스를 물려받아 구체적으로 원하는 대로 바꾸어 사용 가능

4. 다형성

- 다양한 결과를 있다

Subclass(Inheritance)

'장점'

- 부모 클래스의 기능을 재사용

-부모의 기능을 그대로 사용한다

- 부모의 기능을 일부만 사용한다

- 부모에게 없는 기능을 추가 가능하다

'단점'

- 부모가 바뀌면 같이 바뀐다

# composition 방식으로 inheritance 방식 대체 가능하다

duck typing, composition으로 사용해도 상속받은 것과 유사하게 사용 가능하다

 

class A:
  a = 1

class B(A): # Delegat(위임/대리자)
  pass 
  
id(A.a) == id(B.a)
# True

A.a is B.a # B에 없으면 부모의 것을 불러오기 때문에 같아진다 
# True

Python의 모든 객체는 object로 부터 상속 받는다

class A(object, metaclass=type):
  pass
  
dir(A)
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__']
dir(A.__class__)
['__abstractmethods__',
 '__base__',
 '__bases__',
 '__basicsize__',
 '__call__',
 '__class__',
 '__delattr__',
 '__dict__',
 '__dictoffset__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__flags__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__instancecheck__',
 '__itemsize__',
 '__le__',
 '__lt__',
 '__module__',
 '__mro__',
 '__name__',
 '__ne__',
 '__new__',
 '__prepare__',
 '__qualname__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasscheck__',
 '__subclasses__',
 '__subclasshook__',
 '__text_signature__',
 '__weakrefoffset__',
 'mro']

다중 상속

다중 상속이란 2개 이상 클래스를 상속하는 것을 말한다

python은 다중 상속을 지원하는 언어이다

다중 상속은 편한 기능이지만 다이아몬드 문제가 발생할 수 있다

단일 상속인 프로그래밍 언어들이 많음(Java)

class A:
  x = 1

class B:
  x = 2 

class C(A,B):
  pass

# method resolution order (python2.2 부터 지원)

C.mro()
# [__main__.C, __main__.A, __main__.B, object]

C.__mro__
# (__main__.C, __main__.A, __main__.B, object)

C.x # C에 없어서 그 다음 우선순위인 A에서 찾는다 
# 1

다이아몬드 문제

class A:
    pass

class B(A):
    pass

class C(A):
    pass

class D(A, B):
    pass

# 이런 경우 메소드의 실행순서를 정할 수 없다 
# TypeError: Cannot create a consistent method resolution order (MRO) for bases A, B
class A:
    pass

class B(A):
    pass

class C(A):
    pass

class D(B, C):
    pass
    
D.mro() # linearization / tail recursion elimination => linearization
# [__main__.D, __main__.B, __main__.C, __main__.A, object]

dir(D) # metaclass 
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__']

MRO (Method Resolution Order)

메소드 실행 순서를 확인하는 클래스 메소드(인스턴스로 사용 불가)

class A:
    def __init__(self):
        print('A')

class B(A):
    def __init__(self):   #overriding (parent와 가능한 parameter 일치)
        A.__init__(self) 
        print('B')

class C(A):
    def __init__(self):
        A.__init__(self)
        print('C')

class D(B, C):
    def __init__(self):
        B.__init__(self)   # __init__ 함수 
        C.__init__(self)
        print('D')

d = D()  # A가 중복해서 실행되는 문제가 발생한다 
# A
# B
# A
# C
# D

D.mro()
# [__main__.D, __main__.B, __main__.C, __main__.A, object]
import tensorflow as tf 

model = tf.keras.models.Sequential()

model.__class__.mro()
[keras.engine.sequential.Sequential,
 keras.engine.functional.Functional,
 keras.engine.training.Model,
 keras.engine.base_layer.Layer,
 tensorflow.python.module.module.Module,
 tensorflow.python.training.tracking.tracking.AutoTrackable,
 tensorflow.python.training.tracking.base.Trackable,
 keras.utils.version_utils.LayerVersionSelector,
 keras.utils.version_utils.ModelVersionSelector,
 object]

Super - 중복 실행을 방지하는 방법

super는 상속을 전부 실행하지 않는다

class A:
    def __init__(self):
        print('A')

class B(A):
    def __init__(self):     
        super().__init__()  #super()는 부모의 인스턴스다 / python3 방식이다 
        print('B')

class C(A):
    def __init__(self):  
        super(C, self).__init__()        # __init__은 메소드 이다 / python2, python3 방식이다 
        print('C')

class D(B, C):
    def __init__(self):
        super().__init__()
        print('D')
        
d = D()
# A
# C
# B
# D

D.mro()
# [__main__.D, __main__.B, __main__.C, __main__.A, object]

실행 순서는 D -> B -> C -> A 인데 출력 결과는 왜 반대일까? 그 이유는 바로 super사용시 stack에 들어가기 때문이다

※ Stackoverflow

def fib(n):
  return fib(n-1) + fib(n-2) if n>2 else 1  # 재귀적으로 계산되는 중간 과정은 stack영역에 저장된다 => 중간 과정에서 중복되는 연산이 많이 쌓인다 => stack에 많이 쌓여 넘치는 것을 stackoverflow 
                                            # 그러나 python에서는 overflow가 없다 
fib(10) 
# 55
import sys
sys.maxsize
# 9223372036854775807

sys.maxsize + 1 # 정수형은 stackoverflow가 없는 대신에 내부적으로 현재 남아있는 만큼의 가용 메모리를 모두 수 표현에 끌어다 쓴다. 따라서 정수형 연산은 속도(성능)가 매우 느리다 
# 9223372036854775808

sys.float_info
# sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)

0.1 + 0.1 + 0.1 
# 0.30000000000000004

1.7976931348623157e+308  == 1.7976931348623157e+308 + 1 # 근사적으로 계산하기 때문에 속도가 빠르다 
# True

a == a + 1 # infinity

 

Duck Typing

미운오리새끼 이야기에서 유래가 되어 오리가 아닌데 오리처럼 행동을 하면 오리라고 간주한다는 개념이다

타입을 미리 정하지 않고 실행되었을 때 해당 Method들을 확인하여 타입을 정한다

 

'장점'

- 타입에 대해 자유롭다

- 상속을 하지 않아도 상속을 한것처럼 클래스의 메소드 사용이 가능하다

'단점'

- 원치 않는 타입이 들어 경우 오류가 발생할 있다

- 오류 발생시 원인을 찾기 어려울수 있다

- 호환성 떨어짐 => 추상 클래스로 대체

- 코딩 많이 해야

Composition

상속을 하지 않고 클래스내에 객체를 불러와 다른 클래스의 일부 기능을 사용하는 방법.

상속의 위험부담을 줄일 수 있다

class SecurityDoor:
    locked = True

    def __init__(self, number, status):
        self.door = Door(number, status) # Door의 객체를 갖게 한다

    def open(self):
        if self.locked:
            return
        self.door.open()

    def __getattr__(self, attr):        # try except와 비슷
        return getattr(self.door, attr) # Door의 attr를 가져와라 (상속을 비슷하게 사용)


class ComposedDoor:
    def __init__(self, number, status):
        self.door = Door(number, status)

    def __getattr__(self, attr): # 없으면 가져와라
        return getattr(self.door, attr) # 바꾸지 않고 불러와서 사용할 때

Meta class

type => 모든 클래스의 행동을 결정해준다 클래스의 행동을 바꾸고자 할때 사용하는 클래스

 

type('int2', (int,), {}) # __main__ 메소드가 있다면 현재 내 작업 공간에서 만든 것이라는 의미이다 
# __main__.int2

a = type('int2', (int,), {})
type(a)
# type
class A:  # attribute(클래스안에 정의된 모든 것) vs property (descriptor)
  pass

%whos # 현재 작업 공간에 어떤 객체가 있는가 확인  할때 사용 
#
Variable    Type      Data/Info
-------------------------------
A           type      <class '__main__.A'>
a           type      <class '__main__.int2'>
drive       module    <module 'google.colab.dri<...>s/google/colab/drive.py'>
itertools   module    <module 'itertools' (built-in)>

%who_ls
['A', 'a', 'drive', 'itertools']

%lsmagic
Available line magics:
%alias  %alias_magic  %autocall  %automagic  %autosave  %bookmark  %cat  %cd  %clear  %colors  %config  %connect_info  %cp  %debug  %dhist  %dirs  %doctest_mode  %ed  %edit  %env  %gui  %hist  %history  %killbgscripts  %ldir  %less  %lf  %lk  %ll  %load  %load_ext  %loadpy  %logoff  %logon  %logstart  %logstate  %logstop  %ls  %lsmagic  %lx  %macro  %magic  %man  %matplotlib  %mkdir  %more  %mv  %notebook  %page  %pastebin  %pdb  %pdef  %pdoc  %pfile  %pinfo  %pinfo2  %pip  %popd  %pprint  %precision  %profile  %prun  %psearch  %psource  %pushd  %pwd  %pycat  %pylab  %qtconsole  %quickref  %recall  %rehashx  %reload_ext  %rep  %rerun  %reset  %reset_selective  %rm  %rmdir  %run  %save  %sc  %set_env  %shell  %store  %sx  %system  %tb  %tensorflow_version  %time  %timeit  %unalias  %unload_ext  %who  %who_ls  %whos  %xdel  %xmode

Available cell magics:
%%!  %%HTML  %%SVG  %%bash  %%bigquery  %%capture  %%debug  %%file  %%html  %%javascript  %%js  %%latex  %%perl  %%prun  %%pypy  %%python  %%python2  %%python3  %%ruby  %%script  %%sh  %%shell  %%svg  %%sx  %%system  %%time  %%timeit  %%writefile

Automagic is ON, % prefix IS NOT needed for line magics.

A # REPR(Representation / 이름을 부르면 표현해 주세요) => 메모리에 저장되어 있는 것 부른다 
# __main__.A

※ Error

1. NameError : 이름이 없을

2. AttributeError : attribute가 없을 기능 자체가 없을 

import tensorflow as tf 
model = tf.keras.models.Sequential()

model.summary() # 실행하는 시점에 따라서 인스턴스 변수가 다르게 갖기 때문에 / 값이 없기 때문에 valueError 
# ValueError: This model has not yet been built. Build the model first by calling `build()` or calling `fit()` with some data, or specify an `input_shape` argument in the first layer(s) for automatic build.

model(tf.constant([[1,2]]))
# <tf.Tensor: shape=(1, 2), dtype=int32, numpy=array([[1, 2]], dtype=int32)>

model.summary()
#
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
Total params: 0
Trainable params: 0
Non-trainable params: 0
_________________________________________________________________

layer = tf.keras.layers.Dense(1) 
layer.summary()
# AttributeError: 'Dense' object has no attribute 'summary'

class A:
  t = 1
  def tt(self):
    print('t')
getattr(A, 't') # A에서 t접근 해주세요 
# 1
a = A()
getattr(A, 'tt')(a)
# 1

변수와 상수가 구분이 있는 언어라면 상수는 재할당 할 수 없다

python은 변수와 상수를 구분하지 않는다

따라서 tensorflow에서는 이를 구분하기 위해 constant와 variable을 추가적으로 만들었다

cc = tf.constant([1,2,3]) # 상수
dd = tf.Variable([1,2,3]) # 변수 
dd.assign_add([2,3,4])
# <tf.Variable 'UnreadVariable' shape=(3,) dtype=int32, numpy=array([3, 5, 7], dtype=int32)>

import numpy as np
x = np.array([1,2,3])
x 
# array([1, 2, 3])

print(x)
# [1 2 3]

Python의 *

1. number *, **

- 3*3 # 곱하기

- 3**3 # 제곱

2. sequence *

3. assignment (binding) : unpacking

- x,y,*z = 1,2,3,4 # unpacking에서 나머지

4. parameter *name

- def x(*b): # 인자의 갯수 제한을 두지 않는다 return b

5. parameter *

- def name(*,a,b): # keyword only *다음엔 keyword를 사용해야 한다

       return a + b # positional 방식 다음에 keyword 방식을 써야 한다

6. parameter **

- def y(**b): # Variable Keyword return b 7. argument *

- def a(*b):

      return b x = [1,2,3,4] a(*x) # argument unpacking

8. argument **

- def aa(**b):

       return b y = {'a': 1, 'b' : 2} aa(**)

9. import에서 모두

- import *

※ def t(a, *b)이런 형태를 자주 쓰는 이유

def t(*a, **b):

     print(a)

     print(b)

# parameter, argument를 정확히 맞출 필요가 없음, 인터페이스의 유연성을 위해

 

def aa(**b):
  return b

y= {'a':1,'b':2}
z= {'c':2,'a':3}
{**y, **z}
# {'a': 3, 'b': 2, 'c': 2}

 

 

반응형

+ Recent posts