4-4. Descriptor(1)

uoayopยท2021๋…„ 3์›” 27์ผ
0

Leaf์™€ Python

๋ชฉ๋ก ๋ณด๊ธฐ
18/21
post-thumbnail

์ง€๋‚œ ์‹œ๊ฐ„์— ๋ฐฐ์› ๋˜ ๋ฉ”ํƒ€ ํด๋ž˜์Šค๊ฐ€ ๋‚œ์ด๋„ ์ตœ์ƒ์ด์—ˆ์œผ๋‹ˆ,
descriptor๋Š” ์‰ฝ๊ฒŒ ๋Š๊ปด์งˆ๊ฑฐ๋ผ๋Š” ๊ฐ•์‚ฌ๋‹˜์˜ ๋ง์”€,, ์‚ฌ์‹ค์ด์—ˆ๋‹ค. ๐Ÿฅบ

์˜ˆ์ „์— property ๋ฐฐ์šธ ๋•Œ getter, setter๋ฅผ ๋ฐฐ์› ๋‹ค.
์ด ๋‚ด์šฉ์˜ ํ™•์žฅํŒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋˜๋‹ˆ ๋ณต์Šต์„ ํ•˜๊ณ ์˜ค์ž! (link๐Ÿ”—)

getter, setter๊ฐ€ high level์—์„œ ์ž‘๋™ํ•œ๋‹ค๋ฉด, descriptor๋Š” low level์—์„œ ์ž‘๋™ํ•œ๋‹ค.

descriptor๋ฅผ ์ด์šฉํ•ด์„œ ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ๊ฐœ์ž…ํ•ด์„œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด๋ณด์ž

Descriptor

  • ์˜ค๋Š˜์˜ ํ‚ค์›Œ๋“œ
    • Descriptor
    • get, set, del, Property

Descriptor๊ฐ€ ๋ญ”๊ฐ€์š”? ๐Ÿค”

1. ๊ฐ์ฒด์—์„œ ์„œ๋กœ ๋‹ค๋ฅธ ๊ฐ์ฒด๋ฅผ ์†์„ฑ๊ฐ’์œผ๋กœ ๊ฐ€์งˆ ๋•Œ ์‚ฌ์šฉ๋œ๋‹ค.

๋‹ค๋ฅธ ๊ฐ์ฒด์˜ ์†์„ฑ์œผ๋กœ์จ ์กด์žฌํ•  ๋•Œ, ๊ทธ ์†์„ฑ์— ๋Œ€ํ•˜์—ฌ ์ฝ๊ธฐ, ์“ฐ๊ธฐ, ์‚ญ์ œ ๋“ฑ์˜ ๋™์ž‘์„ ํ• ๋•Œ, ๋™์ž‘์— ๋”ฐ๋ผ ๊ฐ ๊ตฌํ˜„๋œ method๊ฐ€ ํ˜ธ์ถœ๋˜๋Š” ๊ฐ์ฒด์ด๋‹ค.

2. read, write, delete ๋“ฑ์„ ๋ฏธ๋ฆฌ ์ •์˜ ๊ฐ€๋Šฅํ•˜๋‹ค.

3. โญ๏ธ ๋‘๊ฐ€์ง€ ์ข…๋ฅ˜๋กœ ๋‚˜๋‰œ๋‹ค.

  • data descriptor (set, del) : ๋ฐ์ดํ„ฐ๋ฅผ ์ง์ ‘ ์“ฐ๊ฑฐ๋‚˜, ์ง€์šฐ๋Š” ์ˆ˜์ •์„ ํ•œ๋‹ค.
  • non-data descriptor (get) : ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ธฐ๋งŒ ํ•œ๋‹ค.

์žฅ์ 

  • ์ฝ๊ธฐ ์ „์šฉ ๊ฐœ์ฒด ์ƒ์„ฑ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ํด๋ž˜์Šค๋ฅผ ์˜๋„ํ•˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ์ƒ์„ฑ ๊ฐ€๋Šฅํ•˜๋‹ค.

๊ธฐ๋ณธ์ ์ธ descriptor ์˜ˆ์ œ

  • ๊ฐ์ฒด ๋‚ด๋ถ€ ์†์„ฑ์— ๋Œ€ํ•ด read๋ฅผ ํ•  __get__, write๋ฅผ ํ•  __set__, delete๋ฅผ ํ•  __delete__
    ์„ธ๊ฐ€์ง€์˜ ๋‚ด๋ถ€ ๋ฉ”์†Œ๋“œ๋ฅผ ์žฌ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

name ์ด๋ผ๋Š” ์†์„ฑ์— ๋Œ€ํ•ด์„œ ์ ‘๊ทผํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด์•˜๋‹ค.

class descriptorEx1(object):
    def __init__(self, name="Default"):
        self.name = name

    def __get__(self, obj, objtype):
        return 'Get Method called -> self : {}, obj : {}, objtype: {}, name: {}'.format(
            self, obj, objtype, self.name)

    def __set__(self, obj, name):
        print('set method called')
        if isinstance(name, str):
            self.name = name
        else:
            # ์กฐ๊ฑด์— ๋”ฐ๋ผ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.
            raise TypeError('Name should be string')

    def __delete__(self, obj):
        print('Delete method called.')
        self.name = None
class Sample1(object):
    name = descriptorEx1()


s1 = Sample1()

# __set__ ํ˜ธ์ถœ
s1.name = 'Descriptor test1'
# set ๋ฉ”์†Œ๋“œ์—์„œ ์•Œ์•„์„œ ๋ฌธ์ž์—ด์ธ์ง€ ์ฒดํฌํ•˜๊ณ ,
# name์„ __init__์—์„œ ํ• ๋‹น์„ ํ•ด์ฃผ๋Š”๋ฐ, 
# ์‹ค์ œ๋ก  Sample1 class ๋‚ด๋ถ€์˜ name์— ํ• ๋‹น์ด ๋œ๋‹ค.

s1.name(10)
# ์˜ˆ์™ธ ๋ฐœ์ƒ ; TypeError: 'str' object is not callable

# [atrr ํ™•์ธ]
# __get__ ํ˜ธ์ถœ
print('ex 1>', s1.name)
# ex 1> Get Method called
# -> self : <__main__.descriptorEx1 object at 0x7f9576ac8fa0>,
# obj : <__main__.Sample1 object at 0x7f9576aa0820>,
# objtype: <class '__main__.Sample1'>,
# name: Descriptor test1

# __ delte__ ํ˜ธ์ถœ
del s1.name

Sample1์˜ name ๋ณ€์ˆ˜๊ฐ€ ์ˆ˜์ •๋  ๋•Œ descriptorEx1 ๋‚ด๋ถ€์˜
__set__, __get__ ๋“ฑ์ด ์•Œ์•„์„œ ํ˜ธ์ถœ๋œ๋‹ค.
โญ๏ธ ๊ธฐ์กด getter, setter๋Š” ๋ณ€์ˆ˜์— ๋”ฐ๋ผ์„œ ๊ฐœ์ˆ˜๋ฅผ ๋Š˜๋ ค์ค˜์•ผ ํ–ˆ์ง€๋งŒ
์œ„์™€ ๊ฐ™์ด descriptor ์ฒ˜๋ฆฌ๋ฅผ ํ•จ์œผ๋กœ์„œ ์ฝ”๋“œ์˜ ์–‘์ด ์ค„์–ด๋“ค๊ฒŒ ๋œ๋‹ค.


Property๋ฅผ ์ด์šฉํ•ด ๊ตฌํ˜„ํ•œ descriptor ์˜ˆ์ œ

์œ„์˜ ์˜ˆ์ œ๋Š” Property๋Š” Property ํด๋ž˜์Šค ๋ณ„๋„๋กœ, ์‚ฌ์šฉ์€ ์‚ฌ์šฉ ํด๋ž˜์Šค ๋ณ„๋„๋กœ ํ˜ธ์ถœ์„ ํ–ˆ๋‹ค.

์ด๋ฒˆ์—” Property๋ฅผ ์ด์šฉํ•ด์„œ ์•Œ์•„์„œ ๋ฒ”์šฉ์ ์ธ ์—ญํ• ์„ ํ•˜๊ฒŒ ๊ตฌํ˜„์„ ํ•ด๋ณด์ž.

Property

class property(fget=None, fset=None, fdel=None, doc=None)

  • ์ธ์ž์˜ ๋ชจ๋“  ๋””ํดํŠธ๊ฐ’์€ None์ด๋‹ค. ์—†์–ด๋„ ๊ทธ๋งŒ์ด๋ผ๋Š” ๋œป์ด๋‹ค.
  • ๋ฉ”์†Œ๋“œ๋ฅผ ๋งŒ๋“ค์–ด์„œ ์ธ์ž๋กœ ๋„ฃ์–ด์ฃผ๋ฉด ๋œ๋‹ค.
    __get__, __set__๊ณผ ๊ฐ™์ด ๋”ฐ๋กœ ์ •ํ•ด์ง„ ๊ทœ์น™์ด ์—†์œผ๋ฏ€๋กœ ๋งˆ์Œ๋Œ€๋กœ ๋„ค์ด๋ฐ ํ•ด์ฃผ์ž.
class DescriptorEx2(object):
    def __init__(self, value):
        self._name = value

    def getVal(self):
        return 'Get method called -> self:{}, name: {}'.format(self, self._name)

    def setVal(self, value):
        print('set method called')
        if isinstance(value, str):
            self._name = value
        else:
            raise TypeError('name should ba string.')

    def delVal(self):
        print('delete method called')
        self._name = None

    # property ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ๋ณ„๋„๋กœ ์ง€์ •ํ•œ get,set,del,doc๋ฅผ ๋„ฃ์–ด์„œ ๋งŒ๋“ค๊ธฐ
    name = property(getVal, setVal, delVal,
                    'Property ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๋Š” name ํ•„๋“œ์ž…๋‹ˆ๋‹ค.')


s2 = DescriptorEx2('Descriptor Test2')

์œ„์˜ ์˜ˆ์ œ์™€ ๋™์ผํ•˜๊ฒŒ ๋™์ž‘ํ•œ๋‹ค.


[์ถœ์ฒ˜]

  1. ์ธํ”„๋Ÿฐ - ๋ชจ๋‘๋ฅผ ์œ„ํ•œ ํŒŒ์ด์ฌ : ํ•„์ˆ˜ ๋ฌธ๋ฒ• ๋ฐฐ์šฐ๊ธฐ Feat. ์˜คํ”ˆ์†Œ์Šค ํŒจํ‚ค์ง€ ๋ฐฐํฌ (Inflearn Original)

  2. Python, descriptor์— ๋Œ€ํ•˜์—ฌ

profile
slow and steady wins the race ๐Ÿข

0๊ฐœ์˜ ๋Œ“๊ธ€