Slide 1

Slide 1 text

PyConJP 2020 Online MetaclassʁDescriptorʁ ׬શʹཧղͨ͠()͋ͳͨʹ 2020.08.29 @everes Python 3.8.5 (default, Jul 21 2020, 10:48:26) [Clang 11.0.3 (clang-1103.0.32.62)] on darwin

Slide 2

Slide 2 text

2005೥

Slide 3

Slide 3 text

lΤϯδχΞ༻ޠͳʹ΋Θ͔Βͳ͍z Ͱௐ΂͍ͯͩ͘͞Ͷ

Slide 4

Slide 4 text

"dunders" (i.e. names with two leading and two trailing underscores) μϯμʔ https://www.python.org/dev/peps/pep-0008/#module-level-dunder-names

Slide 5

Slide 5 text

"dunders" (i.e. names with two leading and two trailing underscores) https://www.python.org/dev/peps/pep-0008/#module-level-dunder-names __init__ __dict__

Slide 6

Slide 6 text

ModelΫϥεͰ__metaclass__Λར༻͍ͯ͠Δ Django https://www.djangoproject.com

Slide 7

Slide 7 text

class ModelBase(type): "Metaclass for all models" def __new__(cls, name, bases, attrs): # If this isn't a subclass of Model, don't do anything special. … https://github.com/django/django/blob/e27211a0deae2f1d402537f0ebb64ad4ccf6a4da/django/core/meta/__init__.py#L394 githubܦ༝Ͱ༰қʹͨͲΕͨi18nϒϥϯνͬͯݺ͹Εͯͨόʔδϣϯʢ2005/12ʣͷ΋ͷ ←typeΛܧঝ͍ͯͯ͠ɺ __new__ ϝιου͕ఆٛ͞Ε͍ͯΔ ϝλΫϥε

Slide 8

Slide 8 text

class Model: __metaclass__ = ModelBase https://github.com/django/django/blob/e27211a0deae2f1d402537f0ebb64ad4ccf6a4da/django/core/meta/__init__.py#L729 githubܦ༝Ͱ༰қʹͨͲΕͨi18nϒϥϯνͬͯݺ͹Εͯͨόʔδϣϯʢ2005/12ʣͷ΋ͷ ϝλΫϥεΛར༻͍ͯ͠ΔΫϥε

Slide 9

Slide 9 text

class Model: __metaclass__ = ModelBase https://github.com/django/django/blob/e27211a0deae2f1d402537f0ebb64ad4ccf6a4da/django/core/meta/__init__.py#L729 githubܦ༝Ͱ༰қʹͨͲΕͨi18nϒϥϯνͬͯݺ͹Εͯͨόʔδϣϯʢ2005/12ʣͷ΋ͷ ← Python2࣌୅͸Ϋϥεଐੑ __metaclass__ ʹϝλΫϥεΛࢦఆͯ͠·ͨ͠ ϝλΫϥεΛར༻͍ͯ͠ΔΫϥε

Slide 10

Slide 10 text

ॅॴ࿥ ༣ศ൪߸ ౎ಓ෎ݝ ࢢ۠ொଜ 1 1008111 ౦ژ౎ ઍ୅ా۠ 2 6308211 ಸྑݝ ಸྑࢢ 3 3291224 ಢ໦ݝ Ԙ୩܊ 4 ॅॴ࿥ ౎ಓ෎ݝ

Slide 11

Slide 11 text

ॅॴ࿥ ༣ศ൪߸ ౎ಓ෎ݝ ࢢ۠ொଜ 1 1008111 ౦ژ౎ ઍ୅ా۠ 2 6308211 ಸྑݝ ಸྑࢢ 3 3291224 ಢ໦ݝ Ԙ୩܊ 4 ←ϑΝΠϧ ॅॴ࿥ ౎ಓ෎ݝ

Slide 12

Slide 12 text

ॅॴ࿥ ༣ศ൪߸ ౎ಓ෎ݝ ࢢ۠ொଜ 1 1008111 ౦ژ౎ ઍ୅ా۠ 2 6308211 ಸྑݝ ಸྑࢢ 3 3291224 ಢ໦ݝ Ԙ୩܊ 4 ←ϑΝΠϧ ↑γʔτ ॅॴ࿥ ౎ಓ෎ݝ

Slide 13

Slide 13 text

ॅॴ࿥ ༣ศ൪߸ ౎ಓ෎ݝ ࢢ۠ொଜ 1 1008111 ౦ژ౎ ઍ୅ా۠ 2 6308211 ಸྑݝ ಸྑࢢ 3 3291224 ಢ໦ݝ Ԙ୩܊ 4 ←ϑΝΠϧ ↑γʔτ ↓ྻ ߦ→ ॅॴ࿥ ౎ಓ෎ݝ

Slide 14

Slide 14 text

ॅॴ࿥ ༣ศ൪߸ ౎ಓ෎ݝ ࢢ۠ொଜ 1 1008111 ౦ژ౎ ઍ୅ా۠ 2 6308211 ಸྑݝ ಸྑࢢ 3 3291224 ಢ໦ݝ Ԙ୩܊ 4 ←ϑΝΠϧ ↑γʔτ ↓ྻ ߦ→ ॅॴ࿥ ౎ಓ෎ݝ RDBMS = Relational Database Management System

Slide 15

Slide 15 text

ॅॴ࿥ ༣ศ൪߸ ౎ಓ෎ݝ ࢢ۠ொଜ 1 1008111 ౦ژ౎ ઍ୅ా۠ 2 6308211 ಸྑݝ ಸྑࢢ 3 3291224 ಢ໦ݝ Ԙ୩܊ 4 ←ϑΝΠϧ ↑γʔτ ↓ྻ ߦ→ ॅॴ࿥ ౎ಓ෎ݝ RDBMS = Relational Database Management System DjangoͷϞσϧ = Object Relational Mapper = O/R Mapper

Slide 16

Slide 16 text

ॅॴ࿥ ༣ศ൪߸ ౎ಓ෎ݝ ࢢ۠ொଜ 1 1008111 ౦ژ౎ ઍ୅ా۠ 2 6308211 ಸྑݝ ಸྑࢢ 3 3291224 ಢ໦ݝ Ԙ୩܊ 4 ౎ಓ෎ݝʢϚελʔσʔλʣ ίʔυ ౎ಓ෎ݝ 13 13 ౦ژ౎ 14 14 ਆಸ઒ݝ 15 15 ৽ׁ 16 γʔτ = ද ʹςʔϒϧ

Slide 17

Slide 17 text

ॅॴ࿥ ༣ศ൪߸ ౎ಓ෎ݝ ࢢ۠ொଜ 1 1008111 ౦ژ౎ ઍ୅ా۠ 2 6308211 ಸྑݝ ಸྑࢢ 3 3291224 ಢ໦ݝ Ԙ୩܊ 4 ౎ಓ෎ݝʢϚελʔσʔλʣ ίʔυ ౎ಓ෎ݝ 13 13 ౦ژ౎ 14 14 ਆಸ઒ݝ 15 15 ৽ׁ 16 ߦ γʔτ = ද ʹςʔϒϧ

Slide 18

Slide 18 text

ॅॴ࿥ ༣ศ൪߸ ౎ಓ෎ݝ ࢢ۠ொଜ 1 1008111 ౦ژ౎ ઍ୅ా۠ 2 6308211 ಸྑݝ ಸྑࢢ 3 3291224 ಢ໦ݝ Ԙ୩܊ 4 ౎ಓ෎ݝʢϚελʔσʔλʣ ίʔυ ౎ಓ෎ݝ 13 13 ౦ژ౎ 14 14 ਆಸ઒ݝ 15 15 ৽ׁ 16 ߦ ྻ γʔτ = ද ʹςʔϒϧ

Slide 19

Slide 19 text

Djangoͷॅॴ࿥Ϟσϧ from django.db import models # Create your models here. class Prefecture(models.Model): code = models.CharField(max_length=2) name = models.CharField(max_length=4) class Address(models.Model): zipcode = models.CharField(max_length=7) prefecture = models.ForeignKey(Prefecture, on_delete=models.PROTECT) name = models.CharField(max_length=25) memo = models.TextField(blank=True) ϞσϧΫϥε ॅॴ࿥ ༣ศ൪߸ ౎ಓ෎ݝ ࢢ۠ொଜ 1 1008111 ౦ژ౎ ઍ୅ా۠ 2 6308211 ಸྑݝ ಸྑࢢ 3 3291224 ಢ໦ݝ Ԙ୩܊ 4 ౎ಓ෎ݝʢϚελʔσʔλʣ ίʔυ ౎ಓ෎ݝ 1 13 ౦ژ౎ 2 14 ਆಸ઒ݝ 3 15 ৽ׁ 4

Slide 20

Slide 20 text

Djangoͷॅॴ࿥Ϟσϧ from django.db import models # Create your models here. class Prefecture(models.Model): code = models.CharField(max_length=2) name = models.CharField(max_length=4) class Address(models.Model): zipcode = models.CharField(max_length=7) prefecture = models.ForeignKey(Prefecture, on_delete=models.PROTECT) name = models.CharField(max_length=25) memo = models.TextField(blank=True) ϞσϧΫϥε from django.db import models # Create your models here. class Prefecture(models.Model): code = models.CharField(max_length=2) name = models.CharField(max_length=4) class Address(models.Model): zipcode = models.CharField(max_length=7) prefecture = models.ForeignKey(Prefecture, on_delete=models.PROTECT) name = models.CharField(max_length=25) memo = models.TextField(blank=True) ॅॴ࿥ ༣ศ൪߸ ౎ಓ෎ݝ ࢢ۠ொଜ 1 1008111 ౦ژ౎ ઍ୅ా۠ 2 6308211 ಸྑݝ ಸྑࢢ 3 3291224 ಢ໦ݝ Ԙ୩܊ 4 ౎ಓ෎ݝʢϚελʔσʔλʣ ίʔυ ౎ಓ෎ݝ 1 13 ౦ژ౎ 2 14 ਆಸ઒ݝ 3 15 ৽ׁ 4

Slide 21

Slide 21 text

Djangoͷॅॴ࿥Ϟσϧ >>> Address.__dict__ mappingproxy({'__module__': 'address.models', '__doc__': 'Address(id, zipcode, prefecture, name, memo)', '_meta': , 'DoesNotExist': address.models.Address.DoesNotExist, 'MultipleObjectsReturned': address.models.Address.MultipleObjectsReturned, 'zipcode': , 'prefecture_id': , 'prefecture': , 'name': , 'memo': , 'id': , 'objects': }) ϞσϧΫϥεͷ __dict__ ← models.CharFieldͰ͸ͳ͘ͳͬͯΔ ϙΠϯτ

Slide 22

Slide 22 text

Djangoͷॅॴ࿥Ϟσϧ >>> kanagawa = Prefecture.objects.get(code='14') >>> address = Address(zipcode='2480012', prefecture=kanagawa, name=‘ח૔ࢢ໾ॴ')

Slide 23

Slide 23 text

Djangoͷॅॴ࿥Ϟσϧ >>> address.__dict__ {'_state': , 'id': None, 'zipcode': '2480012', 'prefecture_id': 14, 'name': 'ח૔ࢢ໾ॴ', 'memo': ''} >>> kanagawa = Prefecture.objects.get(code='14') >>> address = Address(zipcode='2480012', prefecture=kanagawa, name=‘ח૔ࢢ໾ॴ')

Slide 24

Slide 24 text

Djangoͷॅॴ࿥Ϟσϧ >>> address.save() >>> address.__dict__ {'_state': , 'id': 1, 'zipcode': '2480012', 'prefecture_id': 14, 'name': 'ח૔ࢢ໾ॴ', 'memo': ''} >>> kanagawa = Prefecture.objects.get(code='14') >>> address = Address(zipcode='2480012', prefecture=kanagawa, name=‘ח૔ࢢ໾ॴ')

Slide 25

Slide 25 text

>>> del address.zipcode >>> address.__dict__ {'_state': , 'id': 1, 'prefecture_id': 14, 'name': 'ח૔ࢢ໾ॴ', 'memo': ''} Djangoͷॅॴ࿥Ϟσϧ ← zipcode͕ແ͘ͳ͍ͬͯΔ

Slide 26

Slide 26 text

>>> address.zipcode '2480012' ← delͨ͠͸ͣͳͷʹɺΞΫηεͨ͠Β஋͕໭͖ͬͯͨ ϙΠϯτ Djangoͷॅॴ࿥Ϟσϧ >>> address.__dict__ {'_state': , 'id': 1, 'zipcode': '2480012', 'prefecture_id': 14, 'name': 'ח૔ࢢ໾ॴ', 'memo': ''}

Slide 27

Slide 27 text

• ׬ᘳʹཧղͨ͠Metaclassͷ෮श • ATTRIBUTE • CLASS • REUSABLEͳATTRIBUTE Կ͕ى͖ͨͷ͔૝૾ Ͱ͖ΔΑ͏ʹͳΔͧ

Slide 28

Slide 28 text

׬શʹཧղͨ͠ͷ͸ Metaclass

Slide 29

Slide 29 text

class Spam: egg = 'ham' def bake(self): return f"Baked Spam Egg Ham from {self.__class__.__name__}" >>> Spam.__name__ 'Spam' >>> Spam.__bases__ (object,) >>> Spam.__dict__ mappingproxy({'__module__': '__main__', 'egg': 'ham', 'bake': , '__dict__': , '__weakref__': , '__doc__': None}) >>> spam = Spam() >>> spam.bake() 'Baked Spam Egg Ham from Spam' Class PythonͷClassͱtype https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/00.perfect_01.ipynb

Slide 30

Slide 30 text

def bake_method(self): return f"Baked Spam Egg Ham from {self.__class__.__name__}" Spam = type('Spam', (), dict(egg='ham', bake=bake_method)) >>> Spam.__name__ 'Spam' >>> Spam.__bases__ (object,) >>> Spam.__dict__ mappingproxy({'egg': 'ham', 'bake': , '__module__': '__main__', '__dict__': , '__weakref__': , '__doc__': None}) >>> spam = Spam() >>> spam.bake() 'Baked Spam Egg Ham from Spam' type PythonͷClassͱtype https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/00.perfect_02.ipynb

Slide 31

Slide 31 text

class MyMeta(type): def __new__(cls, name, bases, class_dict): print(f"cls: {cls}") print(f"name: {name}") print(f"bases: {bases}") print(f"class_dict: {class_dict}") return type.__new__(cls, name, bases, class_dict) •__prepare__ •__new__ •__init__ https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/00.perfect_02.ipynb

Slide 32

Slide 32 text

class Mine(metaclass=MyMeta): spam = "egg" def __init__(self): self.spam = None def bark(self): return f"bark from {self.__class__.__name__}" cls: bases: () class_dict: { '__module__': '__main__', '__qualname__': 'Mine', 'spam': ‘egg', '__init__': , ‘bark': } https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/00.perfect_02.ipynb

Slide 33

Slide 33 text

ʢଐੑʣ ATTRIBUTE

Slide 34

Slide 34 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_01.ipynb class Spam: def __init__(self, egg): self.egg = egg >>> spam = Spam('ham') >>> spam.__dict__ {'egg': 'ham'} >>> print(spam.egg) ham Πϯελϯεଐੑ

Slide 35

Slide 35 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_01.ipynb class Spam: def __init__(self, egg): self.egg = egg >>> spam = Spam('ham') >>> spam.__dict__ {'egg': 'ham'} >>> print(spam.egg) ham Πϯελϯεଐੑ ← selfͰΞΫηε͢Δݟ׳Εͨ΍ͭ

Slide 36

Slide 36 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_01.ipynb class Spam: def __init__(self, egg): self.egg = egg >>> spam = Spam('ham') >>> spam.__dict__ {'egg': 'ham'} >>> print(spam.egg) ham Πϯελϯεଐੑ ← selfͰΞΫηε͢Δݟ׳Εͨ΍ͭ ← spam.egg Έ͍ͨʹΠϯελϯεܦ༝ͰΞΫηεͰ͖Δ

Slide 37

Slide 37 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_02.ipynb class Spam: egg = "I'm egg" >>> Spam.__dict__ mappingproxy({'__module__': '__main__', 'egg': "I'm egg", '__dict__': , '__weakref__': , '__doc__': None}) Ϋϥεଐੑ

Slide 38

Slide 38 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_02.ipynb class Spam: egg = "I'm egg" >>> Spam.__dict__ mappingproxy({'__module__': '__main__', 'egg': "I'm egg", '__dict__': , '__weakref__': , '__doc__': None}) Ϋϥεଐੑ ← Ϋϥεʹॻ͍ͯ͋Δ΍ͭ

Slide 39

Slide 39 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_02.ipynb >>> spam = Spam() >>> spam.__dict__ {} >>> spam.egg "I'm egg" >>> spam.egg = "ham" >>> spam.__dict__ {'egg': ‘ham'} >>> Spam.__dict__ mappingproxy({'__module__': '__main__', 'egg': "I'm egg", '__dict__': , '__weakref__': , '__doc__': None}) Ϋϥεଐੑ

Slide 40

Slide 40 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_02.ipynb >>> spam = Spam() >>> spam.__dict__ {} >>> spam.egg "I'm egg" >>> spam.egg = "ham" >>> spam.__dict__ {'egg': ‘ham'} >>> Spam.__dict__ mappingproxy({'__module__': '__main__', 'egg': "I'm egg", '__dict__': , '__weakref__': , '__doc__': None}) Ϋϥεଐੑ ← Πϯελϯεͷ __dict__ ʹ͸ݟ౰ͨΒͳ͍

Slide 41

Slide 41 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_02.ipynb >>> spam = Spam() >>> spam.__dict__ {} >>> spam.egg "I'm egg" >>> spam.egg = "ham" >>> spam.__dict__ {'egg': ‘ham'} >>> Spam.__dict__ mappingproxy({'__module__': '__main__', 'egg': "I'm egg", '__dict__': , '__weakref__': , '__doc__': None}) Ϋϥεଐੑ ← Πϯελϯεͷ __dict__ ʹ͸ݟ౰ͨΒͳ͍ ← Πϯελϯεܦ༝Ͱ΋ΞΫηε͸Ͱ͖Δ

Slide 42

Slide 42 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_02.ipynb >>> spam = Spam() >>> spam.__dict__ {} >>> spam.egg "I'm egg" >>> spam.egg = "ham" >>> spam.__dict__ {'egg': ‘ham'} >>> Spam.__dict__ mappingproxy({'__module__': '__main__', 'egg': "I'm egg", '__dict__': , '__weakref__': , '__doc__': None}) Ϋϥεଐੑ ← Πϯελϯεͷ __dict__ ʹ͸ݟ౰ͨΒͳ͍ ← Πϯελϯεܦ༝Ͱ΋ΞΫηε͸Ͱ͖Δ ← ಉ໊ͷΠϯελϯεଐੑΛׂΓ౰ͯΔͱɺΠϯελϯεͷ __dict__ ʹݱΕΔ

Slide 43

Slide 43 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_02.ipynb >>> spam = Spam() >>> spam.__dict__ {} >>> spam.egg "I'm egg" >>> spam.egg = "ham" >>> spam.__dict__ {'egg': ‘ham'} >>> Spam.__dict__ mappingproxy({'__module__': '__main__', 'egg': "I'm egg", '__dict__': , '__weakref__': , '__doc__': None}) Ϋϥεଐੑ ← Πϯελϯεͷ __dict__ ʹ͸ݟ౰ͨΒͳ͍ ← Πϯελϯεܦ༝Ͱ΋ΞΫηε͸Ͱ͖Δ ← ಉ໊ͷΠϯελϯεଐੑΛׂΓ౰ͯΔͱɺΠϯελϯεͷ __dict__ ʹݱΕΔ ← Ϋϥεͷ __dict__ ͷ஋͸ݩͷ··

Slide 44

Slide 44 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_03.ipynb class Spam: egg = "I'm egg" @classmethod def my_egg(cls): return cls.egg >>> Spam.__dict__ mappingproxy({'__module__': '__main__', 'egg': "I'm egg", 'my_egg': , '__dict__': , '__weakref__': , '__doc__': None}) Ϋϥεϝιου

Slide 45

Slide 45 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_03.ipynb >>> spam = Spam() >>> spam.__dict__ {} >>> spam.egg = 'ham' >>> spam.my_egg() "I'm egg" >>> spam.egg 'ham' >>> spam.__dict__ {'egg': 'ham'} Ϋϥεϝιου

Slide 46

Slide 46 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_03.ipynb >>> spam = Spam() >>> spam.__dict__ {} >>> spam.egg = 'ham' >>> spam.my_egg() "I'm egg" >>> spam.egg 'ham' >>> spam.__dict__ {'egg': 'ham'} Ϋϥεϝιου ← Πϯελϯεͷ __dict__ ʹ͸ݟ౰ͨΒͳ͍

Slide 47

Slide 47 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_03.ipynb >>> spam = Spam() >>> spam.__dict__ {} >>> spam.egg = 'ham' >>> spam.my_egg() "I'm egg" >>> spam.egg 'ham' >>> spam.__dict__ {'egg': 'ham'} Ϋϥεϝιου ← Πϯελϯεͷ __dict__ ʹ͸ݟ౰ͨΒͳ͍ ← Πϯελϯεଐੑʹ ‘ham’ ΛׂΓ౰ͯ

Slide 48

Slide 48 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_03.ipynb >>> spam = Spam() >>> spam.__dict__ {} >>> spam.egg = 'ham' >>> spam.my_egg() "I'm egg" >>> spam.egg 'ham' >>> spam.__dict__ {'egg': 'ham'} Ϋϥεϝιου ← Πϯελϯεͷ __dict__ ʹ͸ݟ౰ͨΒͳ͍ ← Πϯελϯεଐੑʹ ‘ham’ ΛׂΓ౰ͯ ← Ϋϥεϝιουܦ༝͸ɺΫϥεଐੑΛࢀর͢Δ

Slide 49

Slide 49 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_03.ipynb >>> spam = Spam() >>> spam.__dict__ {} >>> spam.egg = 'ham' >>> spam.my_egg() "I'm egg" >>> spam.egg 'ham' >>> spam.__dict__ {'egg': 'ham'} Ϋϥεϝιου ← Πϯελϯεͷ __dict__ ʹ͸ݟ౰ͨΒͳ͍ ← Πϯελϯεଐੑʹ ‘ham’ ΛׂΓ౰ͯ ← Ϋϥεϝιουܦ༝͸ɺΫϥεଐੑΛࢀর͢Δ ← Πϯελϯεଐੑ͸ΫϥεଐੑΛࢀরͯ͠΋มΘΒͳ͍

Slide 50

Slide 50 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_04.ipynb class BadSpam: def __init__(self, egg): self.setEgg(egg) def getEgg(self): return self.egg def setEgg(self, egg): self.egg = egg >>> spam = BadSpam('ham') >>> spam.__dict__ {'egg': 'ham'} ϓϩύςΟʢෆཁͳΞΫηα͸࡞Βͳ͍ʣ

Slide 51

Slide 51 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_04.ipynb class BadSpam: def __init__(self, egg): self.setEgg(egg) def getEgg(self): return self.egg def setEgg(self, egg): self.egg = egg >>> spam = BadSpam('ham') >>> spam.__dict__ {'egg': 'ham'} ϓϩύςΟʢෆཁͳΞΫηα͸࡞Βͳ͍ʣ ಈ࡞͢Δ͚ΕͲμϝ PYTHONIC͡Όͳ͍

Slide 52

Slide 52 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_04.ipynb ϓϩύςΟ class GoodSpam: def __init__(self, egg): self.egg = egg >>> spam = GoodSpam('boiled') >>> spam.egg += ‘ double’ 'boiled double' ΧϓηϧԽͱ͔͋ͱ͔ΒͰ͖ΔΜͰ ඞཁʹͳΔ·Ͱແବͳίʔυ͸ॻ͔ͳ͍

Slide 53

Slide 53 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_04.ipynb class GoodSpam: def __init__(self, egg): self.egg = egg @property def egg(self): print('getter of egg') return self._egg @egg.setter def egg(self, egg): print('setter of egg') self._egg = egg ϓϩύςΟ >>> spam = GoodSpam('very good ham') setter of egg >>> spam.__dict__ {'_egg': 'very good ham'} >>> spam.egg getter of egg 'very good ham'

Slide 54

Slide 54 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_04.ipynb class GoodSpam: def __init__(self, egg): self.egg = egg @property def egg(self): print('getter of egg') return self._egg @egg.setter def egg(self, egg): print('setter of egg') self._egg = egg ϓϩύςΟ >>> spam = GoodSpam('very good ham') setter of egg >>> spam.__dict__ {'_egg': 'very good ham'} >>> spam.egg getter of egg 'very good ham' ← ͜ͷ࣌఺Ͱ@egg.setter͕ಈ࡞͍ͯ͠Δ

Slide 55

Slide 55 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_05.ipynb class Spam: def __init__(self, value): self.__value = value def spam_value(self): return self.__value class Egg(Spam): def __init__(self, value): super().__init__("from child: " + value) self.__value = value def egg_value(self): return self.__value ϓϥΠϕʔτʁ >>> egg = Egg('goo') >>> egg.egg_value() 'goo' >>> egg.spam_value() 'from child: goo’ >>> egg.__dict__ {'_Spam__value': 'from child: goo', '_Egg__value': 'goo'}

Slide 56

Slide 56 text

ʢΫϥεܧঝʣ CLASSES

Slide 57

Slide 57 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/02.classes_01.ipynb class BaseClass: def __init__(self): print(“1: I'm BaseClass __init__") class ChildA(BaseClass): def __init__(self): print("1: I'm ChildA __init__") super().__init__() print("2: I'm ChildA __init__") class ChildB(BaseClass): def __init__(self): print("1: I'm ChildB __init__") super().__init__() print("2: I'm ChildB __init__") class Spam(ChildA, ChildB): def __init__(self): print("1: I'm Concrete Class") super().__init__() print("2: I'm Concrete Class") >>> spam = Spam() 1: I'm Concrete Class 1: I'm ChildA __init__ 1: I'm ChildB __init__ 1: I'm BaseClass __init__ 2: I'm ChildB __init__ 2: I'm ChildA __init__ 2: I'm Concrete Class >>> Spam.mro() [__main__.Spam, __main__.ChildA, __main__.ChildB, __main__.BaseClass, object] μΠϠϞϯυܕଟॏܧঝ BaseClass ChildA ChildB Spam ܧঝ

Slide 58

Slide 58 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/02.classes_01.ipynb class BaseClass: def __init__(self): print(“1: I'm BaseClass __init__") class ChildA(BaseClass): def __init__(self): print("1: I'm ChildA __init__") super().__init__() print("2: I'm ChildA __init__") class ChildB(BaseClass): def __init__(self): print("1: I'm ChildB __init__") super().__init__() print("2: I'm ChildB __init__") class Spam(ChildA, ChildB): def __init__(self): print("1: I'm Concrete Class") super().__init__() print("2: I'm Concrete Class") >>> spam = Spam() 1: I'm Concrete Class 1: I'm ChildA __init__ 1: I'm ChildB __init__ 1: I'm BaseClass __init__ 2: I'm ChildB __init__ 2: I'm ChildA __init__ 2: I'm Concrete Class >>> Spam.mro() [__main__.Spam, __main__.ChildA, __main__.ChildB, __main__.BaseClass, object] μΠϠϞϯυܕଟॏܧঝ BaseClass ChildA ChildB Spam ܧঝ ϝιου୳ࡧॱ

Slide 59

Slide 59 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/02.classes_01.ipynb class BaseClass: def called_method(self): print(“1: I'm BaseClass called_method") class ChildA(BaseClass): def called_method(self): print("1: I'm ChildA called_method") super().called_method() print("2: I'm ChildA called_method") class ChildB(BaseClass): def called_method(self): print("1: I'm ChildB called_method") super().called_method() print("2: I'm ChildB called_method") class Spam(ChildA, ChildB): def called_method(self): print("1: I'm Concreat Class") super().called_method() print("2: I'm Concreat Class") >>> spam = Spam() >>> spam.called_method() 1: I'm Concreat Class 1: I'm ChildA called_method 1: I'm ChildB called_method 1: I'm BaseClass called_method 2: I'm ChildB called_method 2: I'm ChildA called_method 2: I'm Concreat Class μΠϠϞϯυܕଟॏܧঝ BaseClass ChildA ChildB Spam ܧঝ ϝιου୳ࡧॱ

Slide 60

Slide 60 text

class BaseClass: def called_method(self, value): print(f”1: I'm BaseClass called_method with {value}") class ChildA(BaseClass): def called_method(self, value): print("1: I'm ChildA called_method") super().called_method("ChildA") print("2: I'm ChildA called_method") class ChildB(BaseClass): def called_method(self, value): print(f"1: I'm ChildB called_method with {value}") super().called_method("ChildB") print("2: I'm ChildB called_method") class Spam(ChildA, ChildB): def called_method(self, value): print("1: I'm Concreat Class") super().called_method(None) print("2: I'm Concreat Class") >>> spam = Spam() >>> spam.called_method(None) 1: I'm Concreat Class 1: I'm ChildA called_method 1: I'm ChildB called_method with ChildA 1: I'm BaseClass called_method with ChildB 2: I'm ChildB called_method 2: I'm ChildA called_method 2: I'm Concreat Class https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/02.classes_01.ipynb μΠϠϞϯυܕଟॏܧঝ BaseClass ChildA ChildB Spam ܧঝ ϝιου୳ࡧॱ

Slide 61

Slide 61 text

class BaseClass: def __init__(self): print("I'm BaseClass __init__") class ChildA(BaseClass): def __init__(self): print("1: I'm ChildA __init__") super().__init__() print("2: I'm ChildA __init__") class ChildB(BaseClass): def __init__(self): print("1: I'm ChildB __init__") super().__init__() print("2: I'm ChildB __init__") class ChildX(ChildA, ChildB): def __init__(self): print("1: I'm ChildX __init__") super().__init__() print("2: I'm ChildX __init__") class ChildY(ChildA, ChildB): def __init__(self): print("1: I'm ChildY __init__") super().__init__() print("2: I'm ChildY __init__") class Spam(ChildX, ChildY): def __init__(self): print("1: I'm Concreat Class") super().__init__() print("2: I'm Concreat Class") >>> spam = Spam() 1: I'm Concreat Class 1: I'm ChildX __init__ 1: I'm ChildY __init__ 1: I'm ChildA __init__ 1: I'm ChildB __init__ I'm BaseClass __init__ 2: I'm ChildB __init__ 2: I'm ChildA __init__ 2: I'm ChildY __init__ 2: I'm ChildX __init__ 2: I'm Concreat Class >>> Spam.mro() [__main__.Spam, __main__.ChildX, __main__.ChildY, __main__.ChildA, __main__.ChildB, __main__.BaseClass, object] https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/02.classes_01.ipynb BaseClass ChildA ChildB Spam ܧঝ ChildX ChildY ϝιου୳ࡧॱ

Slide 62

Slide 62 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/01.attributes_04.ipynb class BaseClass: def __init__(self): print("I'm BaseClass __init__") class ChildA(BaseClass): def __init__(self): print("1: I'm ChildA __init__") super().__init__() print("2: I'm ChildA __init__") class ChildB(BaseClass): def __init__(self): print("1: I'm ChildB __init__") super().__init__() print("2: I'm ChildB __init__") class ChildX(ChildA, ChildB): def __init__(self): print("1: I'm ChildX __init__") super().__init__() print("2: I'm ChildX __init__") class ChildY(ChildB, ChildA): def __init__(self): print("1: I'm ChildY __init__") super().__init__() print("2: I'm ChildY __init__") class Spam(ChildX, ChildY): def __init__(self): print("1: I'm Concreat Class") super().__init__() print("2: I'm Concreat Class") --------------------------------------------------------------------------- TypeError Traceback (most recent call last) in 30 print("2: I'm ChildY __init__") 31 ---> 32 class Spam(ChildX, ChildY): 33 def __init__(self): 34 print("1: I'm Concreat Class") TypeError: Cannot create a consistent method resolution order (MRO) for bases ChildA, ChildB

Slide 63

Slide 63 text

ʢੑ࣭Λ࠶ར༻͍ͨ͠ʣ REUSABLE

Slide 64

Slide 64 text

class Spam: def __init__(self): self.value = 0 @property def value(self): return self._value @value.setter def value(self, value): if type(value) is not int: raise ValueError(f"value should be int, but {value}") if value < 0: raise ValueError(f"value souldn't be negative") self._value = value >>> spam = Spam() >>> spam.value = 1 >>> spam.__dict__ {'_value': 1} https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/03.property_01.ipynb >>> spam.value = "10" --------------------------------------------------------------------------- ValueError Traceback (most recent call last) in ----> 1 spam.value = "10" in value(self, value) 10 def value(self, value): 11 if type(value) is not int: ---> 12 raise ValueError(f"value should be int, but {value}") 13 if value < 0: 14 raise ValueError(f"value souldn't be negative") ValueError: value should be int, but 10

Slide 65

Slide 65 text

class Spam: def __init__(self): self.value = 0 @property def value(self): return self._value @value.setter def value(self, value): if type(value) is not int: raise ValueError(f"value should be int, but {value}") if value < 0: raise ValueError(f"value souldn't be negative") self._value = value >>> spam = Spam() >>> spam.value = 1 >>> spam.__dict__ {'_value': 1} https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/03.property_01.ipynb >>> spam.value = -1 --------------------------------------------------------------------------- ValueError Traceback (most recent call last) in ----> 1 spam.value = -1 in value(self, value) 12 raise ValueError(f"value should be int, but {value}") 13 if value < 0: ---> 14 raise ValueError(f"value souldn't be negative") 15 self._value = value ValueError: value souldn't be negative >>> spam.value = 1 >>> spam.__dict__ {'_value': 1}

Slide 66

Slide 66 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/03.property_02.ipynb def check_positive_integer(value): if type(value) is not int: raise ValueError(f"value should be int, but {value}") if value < 0: raise ValueError(f"value souldn't be negative") class Spam: def __init__(self): self.value1 = 0 self.value2 = 0 @property def value1(self): return self._value1 @value1.setter def value1(self, value): check_positive_integer(value) self._value1 = value @property def value2(self): return self._value2 @value2.setter def value2(self, value): check_positive_integer(value) self._value2 = value

Slide 67

Slide 67 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/03.property_03.ipynb class PositiveInteger: def __init__(self, value): if type(value) is not int: raise ValueError(f"value should be int, but {value}") if value < 0: raise ValueError(f"value souldn't be negative") self.value = value class Spam: def __init__(self): self.value1 = None self.value2 = None >>> spam = Spam() >>> spam.value1 = PositiveInteger(10) >>> spam.value2 = PositiveInteger(20) >>> print(spam.value1.value, spam.value2.value) 10 20

Slide 68

Slide 68 text

class PositiveInteger: def __set__(self, instance, value): if type(value) is not int: raise ValueError(f"value should be int, but {value}") if value < 0: raise ValueError(f"value souldn't be negative") self.value = value def __get__(self, instance, owner): if hasattr(self, 'value'): return self.value return self class Spam: value1 = PositiveInteger() value2 = PositiveInteger() >>> spam = Spam() >>> spam.value1 = 1 >>> spam.value2 = 10 >>> print(spam.value1, spam.value2) 10 20 https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/04.descriptor_01.ipynb >>> spam.value1 = "10" --------------------------------------------------------------------------- ValueError Traceback (most recent call last) … ValueError: value should be int, but 10 >>> spam.value1 = -1 --------------------------------------------------------------------------- ValueError Traceback (most recent call last) … ValueError: value souldn't be negative Descriptor •__set__(self, obj, type=None) •__get__(self, obj, value) •__delete__(self, obj)

Slide 69

Slide 69 text

class PositiveInteger: def __set__(self, instance, value): if type(value) is not int: raise ValueError(f"value should be int, but {value}") if value < 0: raise ValueError(f"value souldn't be negative") self.value = value def __get__(self, instance, owner): if hasattr(self, 'value'): return self.value return self class Spam: value1 = PositiveInteger() value2 = PositiveInteger() https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/04.descriptor_02.ipynb >>> spam1 = Spam() >>> spam1.value1 = 10 >>> spam1.value2 = 20 >>> print(spam1.value1, spam1.value2) 10 20 >>> spam2 = Spam() >>> spam2.value1 = 100 >>> spam2.value2 = 200 >>> print( spam1.value1, spam1.value2, spam2.value1, spam2.value2) 100 200 100 200

Slide 70

Slide 70 text

Πϯελϯε࡞Δͱ͖ʹΞαΠϯ͢Δͷͱಉ໊͡લΛ͚ͭΔ https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/04.descriptor_03.ipynb class PositiveInteger: def __init__(self, name): self.name = name def __set__(self, instance, value): if type(value) is not int: raise ValueError(f"value should be int, but {value}") if value < 0: raise ValueError(f"value souldn't be negative") instance.__dict__[self.name] = value def __get__(self, instance, owner): if not instance: return self try: return instance.__dict__[self.name] except KeyError: raise AttributeError() class Spam: value1 = PositiveInteger("value1") value2 = PositiveInteger("value2") >>> spam1 = Spam() >>> spam1.value1 = 10 >>> spam1.value2 = 20 >>> spam2 = Spam() >>> spam2.value1 = 100 >>> spam2.value2 = 200 >>> print(spam1.value1, spam1.value2, spam2.value1, spam2.value2) 10 20 100 200

Slide 71

Slide 71 text

Πϯελϯε࡞Δͱ͖ʹΞαΠϯ͢Δͷͱಉ໊͡લΛ͚ͭΔ https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/04.descriptor_03.ipynb class PositiveInteger: def __init__(self, name): self.name = name def __set__(self, instance, value): if type(value) is not int: raise ValueError(f"value should be int, but {value}") if value < 0: raise ValueError(f"value souldn't be negative") instance.__dict__[self.name] = value def __get__(self, instance, owner): if not instance: return self try: return instance.__dict__[self.name] except KeyError: raise AttributeError() class Spam: value1 = PositiveInteger("value1") value2 = PositiveInteger("value2") >>> spam1 = Spam() >>> spam1.value1 = 10 >>> spam1.value2 = 20 >>> spam2 = Spam() >>> spam2.value1 = 100 >>> spam2.value2 = 200 >>> print(spam1.value1, spam1.value2, spam2.value1, spam2.value2) 10 20 100 200 value1 = PositiveInteger(“value1”) ᶃ ᶄ

Slide 72

Slide 72 text

class ReusableDescriptor: def set_assigned_name(self, name): self.name = name class PositiveInteger(RecusableDescriptor): def __set__(self, instance, value): if type(value) is not int: raise ValueError(f"value should be int, but {value}") if value < 0: raise ValueError(f"value souldn't be negative") instance.__dict__[self.name] = value def __get__(self, instance, owner): if not instance: return self try: return instance.__dict__[self.name] except KeyError: raise AttributeError() class ReusableDescriptorMetaclass(type): def __new__(meta, name, bases, class_dict): for key, item in class_dict.items(): if isinstance(item, ReusableDescriptor): item.set_assigned_name(key) return type.__new__(meta, name, bases, class_dict) https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/04.descriptor_metaclass_01.ipynb class Spam(metaclass=ReusableDescriptorMetaclass): value1 = PositiveInteger() value2 = PositiveInteger() >>> spam1 = Spam() >>> spam1.value1 = 10 >>> spam1.value2 = 20 >>> spam2 = Spam() >>> spam2.value1 = 100 >>> spam2.value2 = 200 >>> print( spam1.value1, spam1.value2, spam2.value1, spam2.value2) 10 20 100 200 MetaclassͰରԠ͢Δ໊લΛઃఆ͢Δ

Slide 73

Slide 73 text

class ReusableDescriptor: def set_assigned_name(self, name): self.name = name class PositiveInteger(RecusableDescriptor): def __set__(self, instance, value): if type(value) is not int: raise ValueError(f"value should be int, but {value}") if value < 0: raise ValueError(f"value souldn't be negative") instance.__dict__[self.name] = value def __get__(self, instance, owner): if not instance: return self try: return instance.__dict__[self.name] except KeyError: raise AttributeError() class ReusableDescriptorMetaclass(type): def __new__(meta, name, bases, class_dict): for key, item in class_dict.items(): if isinstance(item, ReusableDescriptor): item.set_assigned_name(key) return type.__new__(meta, name, bases, class_dict) https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/04.descriptor_metaclass_01.ipynb class Spam(metaclass=ReusableDescriptorMetaclass): value1 = PositiveInteger() value2 = PositiveInteger() >>> spam1 = Spam() >>> spam1.value1 = 10 >>> spam1.value2 = 20 >>> spam2 = Spam() >>> spam2.value1 = 100 >>> spam2.value2 = 200 >>> print( spam1.value1, spam1.value2, spam2.value1, spam2.value2) 10 20 100 200 MetaclassͰରԠ͢Δ໊લΛઃఆ͢Δ ←ׂΓ౰ͯΒΕͨม਺໊

Slide 74

Slide 74 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/04.descriptor_04.ipynb class SetGetNothingDescriptor: def __set__(self, instance, value): print("SetGetNothingDescriptor.__set__") def __get__(self, instance, owner): print("SetGetNothingDescriptor.__get__") return None class Spam: egg = SetGetNothingDescriptor() def __init__(self): self.ham = SetGetNothingDescriptor() >>> spam = Spam() >>> Spam.__dict__ 'egg': <__main__.SetGetNothingDescriptor at 0x106e09910>, … >>> spam.__dict__ {'ham': <__main__.SetGetNothingDescriptor at 0x106e097f0>} >>> spam.ham <__main__.SetGetNothingDescriptor at 0x106e097f0> >>> spam.ham = 'bacon' >>> spam.__dict__ {'ham': ‘bacon'} >>> spam.ham ‘bacon'

Slide 75

Slide 75 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/04.descriptor_04.ipynb class SetGetNothingDescriptor: def __set__(self, instance, value): print("SetGetNothingDescriptor.__set__") def __get__(self, instance, owner): print("SetGetNothingDescriptor.__get__") return None class Spam: egg = SetGetNothingDescriptor() def __init__(self): self.ham = SetGetNothingDescriptor() >>> spam = Spam() >>> Spam.__dict__ 'egg': <__main__.SetGetNothingDescriptor at 0x106e09910>, … >>> spam.__dict__ {'ham': <__main__.SetGetNothingDescriptor at 0x106e097f0>} >>> spam.ham <__main__.SetGetNothingDescriptor at 0x106e097f0> >>> spam.ham = 'bacon' >>> spam.__dict__ {'ham': ‘bacon'} >>> spam.ham ‘bacon' ← Πϯελϯεͷଐੑʹ

Slide 76

Slide 76 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/04.descriptor_05.ipynb class SetGetNothingDescriptor: def __set__(self, instance, value): print("SetGetNothingDescriptor.__set__") def __get__(self, instance, owner): print("SetGetNothingDescriptor.__get__") return None class Spam: egg = SetGetNothingDescriptor() >>> spam = Spam() >>> Spam.__dict__ 'egg': <__main__.SetGetNothingDescriptor at 0x107a2b910>, … >>> spam.__dict__ {} >>> spam.egg SetGetNothingDescriptor.__get__ >>> spam.egg = ‘ham' SetGetNothingDescriptor.__set__ >>> spam.__dict__ {} # Πϯελϯεଐੑʹ egg Λແཧ໼ཧ௥Ճͯ͠ΈΔ >>> spam.__dict__['egg'] = None >>> spam.__dict__ {'egg': None} >>> spam.egg SetGetNothingDescriptor.__get__ >>> spam.egg = 'ham' SetGetNothingDescriptor.__set__

Slide 77

Slide 77 text

https://docs.python.org/ja/3/reference/datamodel.html#invoking-descriptors “σʔλσεΫϦϓλ͸ɺΠϯελϯεࣙॻ ಺Ͱଐੑ஋͕࠶ఆٛ͞Εͯ΋ɺৗʹ͜ͷ஋ ΛΦʔόʔϥΠυ͠·͢ɻରরత”ʹɺඇ σʔλσεΫϦϓλͷ৔߹ʹ͸ɺଐੑ஋͸ ΠϯελϯεଆͰΦʔόʔϥΠυ͞Ε·͢"

Slide 78

Slide 78 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/04.descriptor_06.ipynb class SetNothingDescriptor: def __set__(self, instance, value): print("SetNothingDescriptor.__set__") class Spam: egg = SetNothingDescriptor() >>> spam = Spam() >>> Spam.__dict__ mappingproxy({'__module__': '__main__', 'egg': <__main__.SetNothingDescriptor at 0x104e11f70>, '__dict__': , '__weakref__': , '__doc__': None}) >>> spam.__dict__ {} >>> spam.egg <__main__.SetNothingDescriptor at 0x104e11f70> >>> spam.egg = 'ham' SetNothingDescriptor.__set__ >>> spam.egg <__main__.SetNothingDescriptor at 0x104e11f70> >>> spam.__dict__['egg'] = 'ham' >>> spam.egg 'ham' >>> spam.egg = 'ham' SetNothingDescriptor.__set__ __set__ / __delete__ ͷ͍ͣΕ͔͕͋Δ৔߹͸ σʔλσΟεΫϦϓλ(data descriptor)

Slide 79

Slide 79 text

https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/04.descriptor_07.ipynb class GetNothingDescriptor: def __get__(self, instance, value): print("GetNothingDescriptor.__get__") class Spam: egg = GetNothingDescriptor() >>> spam = Spam() >>> spam.egg GetNothingDescriptor.__get__ >>> spam.egg = 'ham' >>> spam.__dict__ {'egg': 'ham'} >>> spam.egg 'ham' https://docs.python.org/ja/3/reference/datamodel.html#invoking-descriptors __set__ / __delete__ ͷ͍ͣΕ΋ແ͍৔߹͸ ඇσʔλσΟεΫϦϓλ(non-data descriptor)

Slide 80

Slide 80 text

registry = {} def register(cls): registry[cls.__name__] = cls class AutoRegisterMeta(type): def __new__(cls, name, bases, class_dict): newclass = type.__new__(cls, name, bases, class_dict) register(newclass) return newclass class MyClassA(metaclass=AutoRegisterMeta): pass class MyClassB(metaclass=AutoRegisterMeta): pass >>> registry {'MyClassA': __main__.MyClassA, 'MyClassB': __main__.MyClassB} https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/05_subclasshook_01.ipynb

Slide 81

Slide 81 text

1.ϑΟʔϧυͷఆٛॱ 2.ॳظԽίʔυ 3.:descriptorͷ໊લ 3େ༻్ Metaclass

Slide 82

Slide 82 text

CPython͸Python3.6͔ΒɺPython3.7͔Β͸Pythonͷ࢓༷ͱͯ͠ dictͷॱং͕อূ͞ΕΔΑ͏ʹͳͬͨͷͰ ϝλΫϥε͸ෆཁʹ 1. ϑΟʔϧυͷఆٛॱ

Slide 83

Slide 83 text

3. Descriptorͷ໊લ Python3.6͔Β __set_name__ ͱ͍͏໊લΛ౉ͯ͘͠ΕΔػߏ͕௥Ճʹͳͬͯ ϝλΫϥε͸ෆཁʹ

Slide 84

Slide 84 text

μϯμʔset_name͕࣮͸͋Δɻmetaclassͷ༻్ͷ1͕ͭແ͘ͳͬͨɻ ࣮͸descriptorͱ͸ؔ܎ͳ͘ɺμϯμʔset_name͕͋Ε͹ݺ͹ΕΔ class PositiveInteger: def __set_name__(self, owner, name): self.name = name def __set__(self, instance, value): if type(value) is not int: raise ValueError(f"value should be int, but {value}") if value < 0: raise ValueError(f"value souldn't be negative") instance.__dict__[self.name] = value def __get__(self, instance, owner): if not instance: return self try: return instance.__dict__[self.name] except KeyError: raise AttributeError() class Spam: value1 = PositiveInteger() value2 = PositiveInteger() https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/04.descriptor_metaclass_02.ipynb >>> spam1 = Spam() >>> spam1.value1 = 10 >>> spam1.value2 = 20 >>> spam2 = Spam() >>> spam2.value1 = 100 >>> spam2.value2 = 200 >>> print(spam1.value1, spam1.value2, spam2.value1, spam2.value2) 10 20 100 200 __set_name__ (Python3.6͔Β)

Slide 85

Slide 85 text

αϒΫϥε͕ੜ੒͞ΕͨࡍʹɺαϒΫϥεͱͯ͠ͷཁ݅Λຬ͍ͨͯ͠Δ͔ͷνΣοΫ ΍ɺઌ΄ͲͷϓϥάΠϯγεςϜͷΑ͏ͳ΋ͷ͸SubClassੜ੒࣌ʹݺ͹ΕΔhookɺ __init_subclass__ ͕Python3.6͔Βొ৔ͨ͜͠ͱʹΑΓɺ ϝλΫϥε͸ෆཁʹ SubClassੜ੒࣌hook

Slide 86

Slide 86 text

registry = {} def register(cls): registry[cls.__name__] = cls class RegisterBase: def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) register(cls) class MyClassA(RegisterBase): pass class MyClassB(RegisterBase): pass >>> registry {'MyClassA': __main__.MyClassA, 'MyClassB': __main__.MyClassB} __init_subclass__ https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/05_subclasshook_01.ipynb

Slide 87

Slide 87 text

ผͷϝλΫϥε͕ࢦఆ͞Ε ͨෳ਺ͷΫϥεΛܧঝ͢Δ ͱΤϥʔʹͳΔ͜ͱ͕͋Δ Metaclass

Slide 88

Slide 88 text

from abc import ABC, abstractmethod registry = {} def register(cls): registry[cls.__name__] = cls class AutoRegisterMeta(type): def __new__(cls, name, bases, class_dict): newclass = type.__new__(cls, name, bases, class_dict) register(newclass) return newclass class AbstractPlugin(ABC): @abstractmethod def run(self): pass class MyClassA(AbstractPlugin, metaclass=AutoRegisterMeta): def run(self): return f"return from {self.__class__.__name__}.run" --------------------------------------------------------------------------- TypeError Traceback (most recent call last) in 18 19 ---> 20 class MyClassA(AbstractPlugin, metaclass=AutoRegisterMeta): 21 def run(self): 22 return f"return from {self.__class__.__name__}.run" TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/06_metaclass_01.ipynb

Slide 89

Slide 89 text

͜Ε͸ ͍ͭى͖Δ͔Θ͔Βͳ͍໰୊ ʢ͍ͭͷؒʹ͔࢖ͬͯΔϥΠϒϥϦͷΫϥε͕metaclasΛ࢖͏͔΋஌Εͳ͍ʣ

Slide 90

Slide 90 text

from abc import ABC, abstractmethod registry = {} def register(cls): registry[cls.__name__] = cls class RegisterBase: def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) register(cls) class AbstractPlugin(ABC): @abstractmethod def run(self): pass class MyClassA(AbstractPlugin, RegisterBase): def run(self): return f"return from {self.__class__.__name__}.run" class MyClassB(AbstractPlugin, RegisterBase): def run(self): return f"return from {self.__class__.__name__}.run" >>> registry {'MyClassA': __main__.MyClassA, 'MyClassB': __main__.MyClassB} >>> ma = MyClassA() >>> ma.run() 'return from MyClassA.run' https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/06_metaclass_02.ipynb

Slide 91

Slide 91 text

from abc import ABC, ABCMeta, abstractmethod registry = {} def register(cls): registry[cls.__name__] = cls class AutoRegisterMeta(ABCMeta): def __new__(cls, name, bases, class_dict): newclass = super().__new__(cls, name, bases, class_dict) register(newclass) return newclass class AbstractPlugin(ABC): @abstractmethod def run(self): pass class MyClassA(AbstractPlugin, metaclass=AutoRegisterMeta): def run(self): return f"return from {self.__class__.__name__}.run" class MyClassB(AbstractPlugin, metaclass=AutoRegisterMeta): pass >>> registry {'MyClassA': __main__.MyClassA, 'MyClassB': __main__.MyClassB} >>> mb = MyClassB() --------------------------------------------------------------------------- TypeError Traceback (most recent call last) in ----> 1 mb = MyClassB() TypeError: Can't instantiate abstract class MyClassB with abstract methods run https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/06_metaclass_03.ipynb

Slide 92

Slide 92 text

ͷਓ͸௚઀͸࢖Θͳ͍ͱ͍ΘΕ͍ͯΔϝλΫϥε 99%

Slide 93

Slide 93 text

Djangoͷॅॴ࿥Ϟσϧ >>> Address.__dict__ mappingproxy({'__module__': 'address.models', '__doc__': 'Address(id, zipcode, prefecture, name, memo)', '_meta': , 'DoesNotExist': address.models.Address.DoesNotExist, 'MultipleObjectsReturned': address.models.Address.MultipleObjectsReturned, 'zipcode': , 'prefecture_id': , 'prefecture': , 'name': , 'memo': , 'id': , 'objects': }) ϞσϧΫϥεͷ __dict__ ← models.CharFieldͰ͸ͳ͘ͳͬͯΔ ϙΠϯτ class Address(models.Model): zipcode = models.CharField(max_length=7) prefecture = models.ForeignKey(Prefecture, on_delete=models.PROTECT) name = models.CharField(max_length=25) memo = models.TextField(blank=True)

Slide 94

Slide 94 text

>>> del address.zipcode >>> address.zipcode '2480012' ← delͨ͠͸ͣͳͷʹɺΞΫηεͨ͠Β஋͕໭͖ͬͯͨ ϙΠϯτ Djangoͷॅॴ࿥Ϟσϧ >>> address.__dict__ {'_state': , 'id': 1, 'zipcode': '2480012', 'prefecture_id': 14, 'name': 'ח૔ࢢ໾ॴ', 'memo': ''}

Slide 95

Slide 95 text

• @everes follow me on twitter • גࣜձࣾσΟʔɾΤψɾΤʔ γεςϜຊ෦CTOࣨ • ύʔϑΣΫτPythonʦվగ2൛ʧग़ͯ·͢ ͓·͑୭Αʁ πϢΩϚίτ https://amzn.to/2zaJIiV

Slide 96

Slide 96 text

• @everes follow me on twitter • גࣜձࣾσΟʔɾΤψɾΤʔ γεςϜຊ෦CTOࣨ • ύʔϑΣΫτPythonʦվగ2൛ʧग़ͯ·͢ ͓·͑୭Αʁ πϢΩϚίτ https://amzn.to/2zaJIiV ͑͏ʃ͊͞Μ ͬͯݺΜͰͶʂ