36
PYTHON DESCRIPTOR 이이이이 Moon Yong Joon

파이썬 Descriptor이해하기 20160403

Embed Size (px)

Citation preview

Page 1: 파이썬 Descriptor이해하기 20160403

PYTHONDESCRIPTOR이해하기

Moon Yong Joon

Page 2: 파이썬 Descriptor이해하기 20160403

Descriptor 란

Page 3: 파이썬 Descriptor이해하기 20160403

Descriptor 란 __get__(self, instance, class), __set__(self,

instance, value), __delete__(self, instance ) 3 개의 메소드를 가지는 클래스 Self 는 Descriptor 클래스의 인스턴스 Instance 는 class 로 생성되는 인스턴스 다른 클래스 변수에 descriptor 인스턴스를 할당해서 사용

Page 4: 파이썬 Descriptor이해하기 20160403

Descriptor aggregation 구조인스턴스에서 클래스변수 (aggregation) 를 호출하면 descriptor 인스턴스가 descriptor 메소드를 호출하여 인스턴스 내부 변수와 할당값을 세팅

Descriptorclass

User de-finedclass

변수 Descriptorinstance

User de-fined

instance

_ 변수

__init____get____set__

__delete__

1. 인스턴스 . 변수 호출

2. 클래스 내의 변수 확인3. Descriptor 인스턴스가 get/set 메소드 호출

4. 인스턴스 .__dict__에 _ 변수 할당

Page 5: 파이썬 Descriptor이해하기 20160403

Descriptor aggregation 주의사항 : 변수명사용자 인스턴스 저장되는 변수를 클래스 내부에 정의된 변수와 구별 필요 ( _ 변수명 )

Descriptorclass

User de-finedclass

변수 Descriptorinstance

User de-fined

instance

_ 변수

__init____get____set__

__delete__

1. 인스턴스 . 변수 호출

2. 클래스 내의 변수 확인3. Descriptor 인스턴스가 get/set 메소드 호출

4. 인스턴스 .__dict__에 _ 변수 할당

Page 6: 파이썬 Descriptor이해하기 20160403

Descriptor aggregation 주의사항 : get

인스턴스 . 변수명을 사용하면 __get__ 가 호출되므로 인스턴스 ._ 변수명이 미존재하므로 초기값 정의 필요

Descriptorclass

User de-finedclass

변수 Descriptorinstance

User de-fined

instance

_ 변수

__init____get____set__

__delete__

1. 인스턴스 . 변수 호출

2. 클래스 내의 변수 확인3. Descriptor 인스턴스가 get/set 메소드 호출

4. 인스턴스 .__dict__에 _ 변수 할당

Page 7: 파이썬 Descriptor이해하기 20160403

Descriptor 상속 구조Descriptor 상속관계로 구현시 실제 descriptor 인스턴스에 값을 보관하여 처 , 실제 __get__ 을 사용해서 값을 검색해야 함

Descriptorclass

User de-finedclass

User de-fined

instance

변수

__init____get__

Descriptor인스턴스 변수

Page 8: 파이썬 Descriptor이해하기 20160403

Descriptor 상속 구조 사용 descriptor 클래스를 상속해서 get 만 사용하는 경우

class D(object) : def __init__(self, x=None) : self.x = x def __get__(self,instance=None,cls=None) : return self.x class D1(D) : def __init__(self, x,y) : self.x = D(x) self.y = D(y)

d1 = D1(2,3)print (" d1")print (d1.__dict__)print (d1.x)print(d1.y)print (" direct call",d1.x.__get__(), d1.y.__get__())print (" Class binding call x ", D1.__get__(d1.x,d1.x))print (" Class binding call y ", D1.__get__(d1.y,d1.y))print (" class binding",type(d1).__get__(d1.x,d1.x))

D1{'x': <__main__.D object at 0x113f46400>, 'y': <__main__.D object at 0x113f461d0>}<__main__.D object at 0x113f46400><__main__.D object at 0x113f461d0> direct call 2 3 Class binding call x 2 Class binding call y 3 class binding 2

Page 9: 파이썬 Descriptor이해하기 20160403

Descriptor 구성

Page 10: 파이썬 Descriptor이해하기 20160403

Descriptor 처리 절차Descriptor class 를 생성하여 실제 구현 클래스 내부의 속성에 대한 init/getter/setter/deleter 를 통제할 수 있도록 구조화

Class Decriptor : def __init__ def __get__ def __set__ def __del__

class Person() : name= Descriptor()

user = Person()user.name = ‘Dahl’

Descriptor class 생성구현 class 정의시 속성에 대한인스턴스 생성

구현 class 에 대한 인스턴스 생성 및 인스턴스 속성에 값 세팅

Page 11: 파이썬 Descriptor이해하기 20160403

Descriptor class 정의클래스에 인스턴스를 관리하는 생성자와 실제 인스턴스의 값을 관리 get/set/delete 메소드 정의

class Descriptor(object) def __init__(self,name):

self.name = ‘_’ + str(name)

def __get__(self, instance, owner): return instance.__dict__[self.name]

def __set__(self, instance, value): instance.__dict__[self.name] = value def __delete__(self, instance): del instance.__dict__[self.name]

인스턴스 객체의 변수명으로 세팅될 변수명 저장

Page 12: 파이썬 Descriptor이해하기 20160403

__get__/__set__ 검색 1 getattr,setattr 함수를 이용해서 내부의 값을 직접 검색 및 갱신

def __get__(self, instance, owner): return getattr(instance, self.name) def __set__(self, instance, value): setattr(instance,self.name,value)

Page 13: 파이썬 Descriptor이해하기 20160403

__get__/__set__ 검색 2 Instance 내부의 값을 직접 검색

def __get__(self, instance, owner): return instance.__dict__[self.name] def __set__(self, instance, value): instance.__dict__[self.name] = value

Page 14: 파이썬 Descriptor이해하기 20160403

Data descriptor 여부 확인 Inspect 모듈을 이용해서 data descriptor 여부 확인

import inspectprint(inspect.isdatadescriptor(descriptor()) #true

Page 15: 파이썬 Descriptor이해하기 20160403

Descriptor 사용 class 정의 / 실행1. 구현 클래스에는 속성에 descriptor 인스턴스 생성2. 인스턴스 객체의 변수에 직접 값을 할당 (set)3. 인스턴스 객체의 변수로 조회 (get)

class Person(object): name = Descriptor(“name”)

user = Person()

print user.__dict__ # {}user.name = 'john smith‘print user.__dict__ # {'_name': 'john smith'}print "user.name ",user.name # user.name john smith

인스턴스의 변수와 내부 관리 변수의 name 을 상이하게 관리도 가능

Page 16: 파이썬 Descriptor이해하기 20160403

Descriptor 메소드 호출 예시2 개 호출 처리는 동일 한 결과가 나온다 .실제 구현시 첫번째 방식이 가독성이 높음

user = Person()

user.name = 'john smith’ # Descriptor.__set__ 호출print user.name # Descriptor.__get__ 호출print user.__dict__

user1 = Person()

Descriptor.__set__(Descriptor(),user1,"dahl")Descriptor.__get__(Descriptor(),user1,Descriptor)print user1.__dict__

Page 17: 파이썬 Descriptor이해하기 20160403

Descriptor : 함수처리

Page 18: 파이썬 Descriptor이해하기 20160403

Descriptor 정의Descriptor 클래스 정의

class descriptor(object) : def __init__(self,name,func): self.name = '_'+ str(name) self.func = func def __get__(self, instance, owner): return getattr(instance,self.name) def __set__(self, instance, value): setattr(instance,self.name,value)

Value 에 함수 할당

Page 19: 파이썬 Descriptor이해하기 20160403

Descriptor 실행함수를 정의하고 할당해서 실행

def add(x,y) : return x+y class A(object) : add = descriptor("add",add) a = A()print(a.__dict__)print(A.__dict__)print(a.add(5,5))print(type(a.__dict__['_add']))print(A.__dict__['add'])print(a.add)print(A.__dict__['add'].__dict__)da = descriptor("aaa","")print(type(da), da.__dict__, da.name)

{}{'add': <__main__.descriptor object at 0x114140358>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__', '__doc__': None}{'_add': <function add at 0x114135730>}10<class 'function'><__main__.descriptor object at 0x114140358><function add at 0x114135730>{'name': '_add', 'func': <function add at 0x114135730>}<class '__main__.descriptor'> {'name': '_aaa', 'func': ''} _aaa

Page 20: 파이썬 Descriptor이해하기 20160403

Descriptor 종류

Page 21: 파이썬 Descriptor이해하기 20160403

Descriptor 종류Method descriptor 와 Data descripter 로 구분

Method descriptor 는 __get__(self, instance, owner) 구현

Data descriptor 는 __get__(self, instance, owner), __set__(self,instance, value) or

__delete__(self, instance) 구현

Page 22: 파이썬 Descriptor이해하기 20160403

Creating data descriptorData Descriptor 클래스를 생성해서 처리하는 방법

class Descriptor(object): def __init__(self): self._name = '' def __get__(self, instance, owner): print "Getting: %s" % self._name return self._name def __set__(self, instance, name): print "Setting: %s" % name self._name = name.title() def __delete__(self, instance): print "Deleting: %s" %self._name del self._name

class Person(object): name = Descriptor()

>>> user = Person() >>> user.name = 'john smith' Setting: john smith >>> user.name Getting: John Smith 'John Smith‘>>> del user.name Deleting: John Smith

Page 23: 파이썬 Descriptor이해하기 20160403

여러 개 속성변수 처리하기

Page 24: 파이썬 Descriptor이해하기 20160403

Descriptor : init 메소드 수정 Descriptor class 에 생성자 메소드에 변수명 , 변수 타입 , 변수값을 받아 생성하도록 만듦

Class Decriptor : # 변수명 , 값타입 , 디폴트값 ,

def __init__(self, var_name, var_type, var_default) : self.var_name = “_”+var_name self.var_type = var_type self.var_default = var_default if var_default else type()

Page 25: 파이썬 Descriptor이해하기 20160403

Descriptor : get/set 함수 수정 Descriptor class 에 생성자 메소드에 변수명 , 변수 타입 , 변수값을 받아 생성하도록 만듦

Class Decriptor : def __get__(self, instance, cls): return getattr(instance, self.name, self.default)

def __set__(self,instance,value): if not isinstance(value,self.type): raise TypeError("Must be a %s" % self.type) setattr(instance,self.name,value)

def __delete__(self,instance): raise AttributeError("Can't delete attribute")

# 처리시 주의 사항 setattr(instance,self.name,value) self.name 의 “ _” 없는 값을 넣을 경우 런타임오류 발생RuntimeError: maximum recursion depth exceeded while calling a Python ob-ject

Page 26: 파이썬 Descriptor이해하기 20160403

구현 class 처리 : 클래스 내부의 변수에 descriptor 인스턴스를 생성함 .

class Person(object): name =Descriptor ("name",str) age = Descriptor("age",int,42) user = Person()

Page 27: 파이썬 Descriptor이해하기 20160403

Property 처리

Page 28: 파이썬 Descriptor이해하기 20160403

Creating Property- 객체 직접 정의 (1)

인스턴스 객체의 변수 접근을 메소드로 제약하기 위해서는 Property 객체로 인스턴스 객체의 변수를 Wrapping 해야 함property(fget=None, fset=None, fdel=None, doc=None)

class P:

def __init__(self,x): self.x = x def getx(self) : return self.x def setx(self, x) : self.x = x def delx(self) : del self.x

x = property(getx,setx,delx," property test ")

Getter, setter, deleter 메소드를 정의

인스턴스 객체의 변수명과 동일하게 Property 객체 생성 ( 내부에 _x 생김 )

Page 29: 파이썬 Descriptor이해하기 20160403

Creating Property– 객체 직접 정의 (2)

실제 인스턴스 객체의 변수에 접근하면 Property 객체의 메소드를 호출하여 처리되고 인스턴스 객체의 변수값이 변경됨

p1 = P(1001)print id(p1.x)print P.__dict__['x']print id(p1.__dict__['x'])print p1.xp1.x = -12print p1.xprint p1.__dict__

# 처리결과값44625868<property object at 0x02C1D4E0>446258681001-12{'x': -12}

Page 30: 파이썬 Descriptor이해하기 20160403

Decorator 처리

Page 31: 파이썬 Descriptor이해하기 20160403

Creating Property decorator(1)

인스턴스 객체의 변수 접근을 메소드로 제약하기 위해서는 Property 객체로 인스턴스 객체의 변수를 Wrapping 해야 함property(fget=None, fset=None, fdel=None, doc=None)

class P: def __init__(self,x): self._x = x

@property def x(self): return self._x @x.setter def x(self, x): self._x = x @x.deleter def x(self): del self._x

Getter, setter, deleter 메소드를 정의인스턴스 객체의 변수명과 동일하게 Property 객체 생성 ( 내부에 _x 생김 )

Page 32: 파이썬 Descriptor이해하기 20160403

Creating Property decorator(2)

Property 객체 생성하여 처리하는 방식과 동일

p1 = P(1001)print(id(p1.x))print(p1.x)p1.x = -12print (p1.x)print (p1.__dict__)

# 처리결과값46261915041001-12{'_x': -12}

Page 33: 파이썬 Descriptor이해하기 20160403

내장함수를 통한 객체접근

Page 34: 파이썬 Descriptor이해하기 20160403

Built-in 내장함수내장함수를 이용하여 객체의 속성에 대한 접근

object.x getattr()object.x = value setattr()del(object.x) delattr()

함수 구조getattr(object, name[, default])setattr(object, name, value)delattr(object, name)hasattr(object, name)callable(object)

Page 35: 파이썬 Descriptor이해하기 20160403

Built-in 내장함수 : 예시 1객체의 속성을 접근하고변경

class A(): def __init__(self, name,age) : self.name = name self.age = age a = A('dahl',50)

if hasattr(a,"name") : print getattr(a,"name") setattr(a,"name","Moon") print getattr(a,"name")else : pass

if hasattr(a,"age") : print getattr(a,"age")else : pass

# 처리결과값dahlMoon50

Page 36: 파이썬 Descriptor이해하기 20160403

Built-in 내장함수 : 예시 2메소드 및 함수여부 확인 후 실행

class A(): def __init__(self, name,age) : self.name = name self.age = age def in_set(self,name,default) : self.__dict__[name] = default print self.__dict__[name]

a = A('dahl',50)

def add(x,y) : return x+y if callable(add) : add(5,6)else : pass

if callable(a.in_set) : a.in_set('age',20)else: pass

# 처리결과값dahlMoon5020