Metaclass?Descriptor?完全に理解した()あなたに / pyconjp2020

Metaclass?Descriptor?完全に理解した()あなたに / pyconjp2020

MetaclassやDescriptorについて概要を知ってる状態からなんとなくわかった状態になるために、順を追ってみていきます。

PyConJP 2020 Online の招待講演資料です
- https://pycon.jp/2020/timetable/?id=213322

サンプルコードのjupyter-notebookファイルはGitHubにあります。
- https://github.com/tsuyukimakoto/pyconjp2020

A49edf2521f9e75826c6546c7585f17a?s=128

makoto tsuyuki

August 29, 2020
Tweet

Transcript

  1. 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
  2. 2005೥

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

  4. "dunders" (i.e. names with two leading and two trailing underscores)

    μϯμʔ https://www.python.org/dev/peps/pep-0008/#module-level-dunder-names
  5. "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__
  6. ModelΫϥεͰ__metaclass__Λར༻͍ͯ͠Δ Django https://www.djangoproject.com

  7. 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__ ϝιου͕ఆٛ͞Ε͍ͯΔ ϝλΫϥε
  8. class Model: __metaclass__ = ModelBase https://github.com/django/django/blob/e27211a0deae2f1d402537f0ebb64ad4ccf6a4da/django/core/meta/__init__.py#L729 githubܦ༝Ͱ༰қʹͨͲΕͨi18nϒϥϯνͬͯݺ͹Εͯͨόʔδϣϯʢ2005/12ʣͷ΋ͷ ϝλΫϥεΛར༻͍ͯ͠ΔΫϥε

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

    ʹϝλΫϥεΛࢦఆͯ͠·ͨ͠ ϝλΫϥεΛར༻͍ͯ͠ΔΫϥε
  10. ॅॴ࿥ ༣ศ൪߸ ౎ಓ෎ݝ ࢢ۠ொଜ 1 1008111 ౦ژ౎ ઍ୅ా۠ 2 6308211

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

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

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

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

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

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

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

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

    ಸྑݝ ಸྑࢢ 3 3291224 ಢ໦ݝ Ԙ୩܊ 4 ౎ಓ෎ݝʢϚελʔσʔλʣ ίʔυ ౎ಓ෎ݝ 13 13 ౦ژ౎ 14 14 ਆಸ઒ݝ 15 15 ৽ׁ 16 ߦ ྻ γʔτ = ද ʹςʔϒϧ
  19. 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
  20. 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
  21. Djangoͷॅॴ࿥Ϟσϧ >>> Address.__dict__ mappingproxy({'__module__': 'address.models', '__doc__': 'Address(id, zipcode, prefecture, name,

    memo)', '_meta': <Options for Address>, 'DoesNotExist': address.models.Address.DoesNotExist, 'MultipleObjectsReturned': address.models.Address.MultipleObjectsReturned, 'zipcode': <django.db.models.query_utils.DeferredAttribute at 0x106706970>, 'prefecture_id': <django.db.models.fields.related_descriptors.ForeignKeyDeferredAttribute at 0x1068b1040>, 'prefecture': <django.db.models.fields.related_descriptors.ForwardManyToOneDescriptor at 0x1068b10a0>, 'name': <django.db.models.query_utils.DeferredAttribute at 0x1068b1190>, 'memo': <django.db.models.query_utils.DeferredAttribute at 0x1068b11f0>, 'id': <django.db.models.query_utils.DeferredAttribute at 0x1068b1310>, 'objects': <django.db.models.manager.ManagerDescriptor at 0x1068b1250>}) ϞσϧΫϥεͷ __dict__ ← models.CharFieldͰ͸ͳ͘ͳͬͯΔ ϙΠϯτ
  22. Djangoͷॅॴ࿥Ϟσϧ >>> kanagawa = Prefecture.objects.get(code='14') >>> address = Address(zipcode='2480012', prefecture=kanagawa,

    name=‘ח૔ࢢ໾ॴ')
  23. Djangoͷॅॴ࿥Ϟσϧ >>> address.__dict__ {'_state': <django.db.models.base.ModelState at 0x107956220>, 'id': None, 'zipcode':

    '2480012', 'prefecture_id': 14, 'name': 'ח૔ࢢ໾ॴ', 'memo': ''} >>> kanagawa = Prefecture.objects.get(code='14') >>> address = Address(zipcode='2480012', prefecture=kanagawa, name=‘ח૔ࢢ໾ॴ')
  24. Djangoͷॅॴ࿥Ϟσϧ >>> address.save() >>> address.__dict__ {'_state': <django.db.models.base.ModelState at 0x107956220>, 'id':

    1, 'zipcode': '2480012', 'prefecture_id': 14, 'name': 'ח૔ࢢ໾ॴ', 'memo': ''} >>> kanagawa = Prefecture.objects.get(code='14') >>> address = Address(zipcode='2480012', prefecture=kanagawa, name=‘ח૔ࢢ໾ॴ')
  25. >>> del address.zipcode >>> address.__dict__ {'_state': <django.db.models.base.ModelState at 0x107956220>, 'id':

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

    <django.db.models.base.ModelState at 0x107956220>, 'id': 1, 'zipcode': '2480012', 'prefecture_id': 14, 'name': 'ח૔ࢢ໾ॴ', 'memo': ''}
  27. • ׬ᘳʹཧղͨ͠Metaclassͷ෮श • ATTRIBUTE • CLASS • REUSABLEͳATTRIBUTE Կ͕ى͖ͨͷ͔૝૾ Ͱ͖ΔΑ͏ʹͳΔͧ

  28. ׬શʹཧղͨ͠ͷ͸ Metaclass

  29. 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': <function __main__.Spam.bake(self)>, '__dict__': <attribute '__dict__' of 'Spam' objects>, '__weakref__': <attribute '__weakref__' of 'Spam' objects>, '__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
  30. 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': <function __main__.bake_method(self)>, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Spam' objects>, '__weakref__': <attribute '__weakref__' of 'Spam' objects>, '__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
  31. 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
  32. class Mine(metaclass=MyMeta): spam = "egg" def __init__(self): self.spam = None

    def bark(self): return f"bark from {self.__class__.__name__}" cls: <class '__main__.MyMeta'> bases: () class_dict: { '__module__': '__main__', '__qualname__': 'Mine', 'spam': ‘egg', '__init__': <function Mine.__init__ at 0x1118481f0>, ‘bark': <function Mine.bark at 0x111848280>} https://github.com/tsuyukimakoto/pyconjp2020/blob/master/notebook/00.perfect_02.ipynb
  33. ʢଐੑʣ ATTRIBUTE

  34. 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 Πϯελϯεଐੑ
  35. 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ͰΞΫηε͢Δݟ׳Εͨ΍ͭ
  36. 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 Έ͍ͨʹΠϯελϯεܦ༝ͰΞΫηεͰ͖Δ
  37. 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__': <attribute '__dict__' of 'Spam' objects>, '__weakref__': <attribute '__weakref__' of 'Spam' objects>, '__doc__': None}) Ϋϥεଐੑ
  38. 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__': <attribute '__dict__' of 'Spam' objects>, '__weakref__': <attribute '__weakref__' of 'Spam' objects>, '__doc__': None}) Ϋϥεଐੑ ← Ϋϥεʹॻ͍ͯ͋Δ΍ͭ
  39. 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__': <attribute '__dict__' of 'Spam' objects>, '__weakref__': <attribute '__weakref__' of 'Spam' objects>, '__doc__': None}) Ϋϥεଐੑ
  40. 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__': <attribute '__dict__' of 'Spam' objects>, '__weakref__': <attribute '__weakref__' of 'Spam' objects>, '__doc__': None}) Ϋϥεଐੑ ← Πϯελϯεͷ __dict__ ʹ͸ݟ౰ͨΒͳ͍
  41. 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__': <attribute '__dict__' of 'Spam' objects>, '__weakref__': <attribute '__weakref__' of 'Spam' objects>, '__doc__': None}) Ϋϥεଐੑ ← Πϯελϯεͷ __dict__ ʹ͸ݟ౰ͨΒͳ͍ ← Πϯελϯεܦ༝Ͱ΋ΞΫηε͸Ͱ͖Δ
  42. 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__': <attribute '__dict__' of 'Spam' objects>, '__weakref__': <attribute '__weakref__' of 'Spam' objects>, '__doc__': None}) Ϋϥεଐੑ ← Πϯελϯεͷ __dict__ ʹ͸ݟ౰ͨΒͳ͍ ← Πϯελϯεܦ༝Ͱ΋ΞΫηε͸Ͱ͖Δ ← ಉ໊ͷΠϯελϯεଐੑΛׂΓ౰ͯΔͱɺΠϯελϯεͷ __dict__ ʹݱΕΔ
  43. 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__': <attribute '__dict__' of 'Spam' objects>, '__weakref__': <attribute '__weakref__' of 'Spam' objects>, '__doc__': None}) Ϋϥεଐੑ ← Πϯελϯεͷ __dict__ ʹ͸ݟ౰ͨΒͳ͍ ← Πϯελϯεܦ༝Ͱ΋ΞΫηε͸Ͱ͖Δ ← ಉ໊ͷΠϯελϯεଐੑΛׂΓ౰ͯΔͱɺΠϯελϯεͷ __dict__ ʹݱΕΔ ← Ϋϥεͷ __dict__ ͷ஋͸ݩͷ··
  44. 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': <classmethod at 0x110a4a460>, '__dict__': <attribute '__dict__' of 'Spam' objects>, '__weakref__': <attribute '__weakref__' of 'Spam' objects>, '__doc__': None}) Ϋϥεϝιου
  45. 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'} Ϋϥεϝιου
  46. 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__ ʹ͸ݟ౰ͨΒͳ͍
  47. 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’ ΛׂΓ౰ͯ
  48. 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’ ΛׂΓ౰ͯ ← Ϋϥεϝιουܦ༝͸ɺΫϥεଐੑΛࢀর͢Δ
  49. 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’ ΛׂΓ౰ͯ ← Ϋϥεϝιουܦ༝͸ɺΫϥεଐੑΛࢀর͢Δ ← Πϯελϯεଐੑ͸ΫϥεଐੑΛࢀরͯ͠΋มΘΒͳ͍
  50. 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'} ϓϩύςΟʢෆཁͳΞΫηα͸࡞Βͳ͍ʣ
  51. 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͡Όͳ͍
  52. 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' ΧϓηϧԽͱ͔͋ͱ͔ΒͰ͖ΔΜͰ ඞཁʹͳΔ·Ͱແବͳίʔυ͸ॻ͔ͳ͍
  53. 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'
  54. 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͕ಈ࡞͍ͯ͠Δ
  55. 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'}
  56. ʢΫϥεܧঝʣ CLASSES

  57. 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 ܧঝ
  58. 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 ܧঝ ϝιου୳ࡧॱ
  59. 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 ܧঝ ϝιου୳ࡧॱ
  60. 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 ܧঝ ϝιου୳ࡧॱ
  61. 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 ϝιου୳ࡧॱ
  62. 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) <ipython-input-17-7b3e0c160262> in <module> 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
  63. ʢੑ࣭Λ࠶ར༻͍ͨ͠ʣ REUSABLE

  64. 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) <ipython-input-3-e924802b48db> in <module> ----> 1 spam.value = "10" <ipython-input-1-abf3b6d8088e> 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
  65. 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) <ipython-input-4-3b6deb05698c> in <module> ----> 1 spam.value = -1 <ipython-input-1-abf3b6d8088e> 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}
  66. 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
  67. 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
  68. 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)
  69. 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
  70. Πϯελϯε࡞Δͱ͖ʹΞαΠϯ͢Δͷͱಉ໊͡લΛ͚ͭΔ 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
  71. Πϯελϯε࡞Δͱ͖ʹΞαΠϯ͢Δͷͱಉ໊͡લΛ͚ͭΔ 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”) ᶃ ᶄ
  72. 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ͰରԠ͢Δ໊લΛઃఆ͢Δ
  73. 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ͰରԠ͢Δ໊લΛઃఆ͢Δ ←ׂΓ౰ͯΒΕͨม਺໊
  74. 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'
  75. 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' ← Πϯελϯεͷଐੑʹ
  76. 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__
  77. https://docs.python.org/ja/3/reference/datamodel.html#invoking-descriptors “σʔλσεΫϦϓλ͸ɺΠϯελϯεࣙॻ ಺Ͱଐੑ஋͕࠶ఆٛ͞Εͯ΋ɺৗʹ͜ͷ஋ ΛΦʔόʔϥΠυ͠·͢ɻରরత”ʹɺඇ σʔλσεΫϦϓλͷ৔߹ʹ͸ɺଐੑ஋͸ ΠϯελϯεଆͰΦʔόʔϥΠυ͞Ε·͢"

  78. 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__': <attribute '__dict__' of 'Spam' objects>, '__weakref__': <attribute '__weakref__' of 'Spam' objects>, '__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)
  79. 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)
  80. 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
  81. 1.ϑΟʔϧυͷఆٛॱ 2.ॳظԽίʔυ 3.:descriptorͷ໊લ 3େ༻్ Metaclass

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

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

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

  86. 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
  87. ผͷϝλΫϥε͕ࢦఆ͞Ε ͨෳ਺ͷΫϥεΛܧঝ͢Δ ͱΤϥʔʹͳΔ͜ͱ͕͋Δ Metaclass

  88. 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) <ipython-input-19-daaad246990d> in <module> 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
  89. ͜Ε͸ ͍ͭى͖Δ͔Θ͔Βͳ͍໰୊ ʢ͍ͭͷؒʹ͔࢖ͬͯΔϥΠϒϥϦͷΫϥε͕metaclasΛ࢖͏͔΋஌Εͳ͍ʣ

  90. 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
  91. 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) <ipython-input-15-f914df2d7a56> in <module> ----> 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
  92. ͷਓ͸௚઀͸࢖Θͳ͍ͱ͍ΘΕ͍ͯΔϝλΫϥε 99%

  93. Djangoͷॅॴ࿥Ϟσϧ >>> Address.__dict__ mappingproxy({'__module__': 'address.models', '__doc__': 'Address(id, zipcode, prefecture, name,

    memo)', '_meta': <Options for Address>, 'DoesNotExist': address.models.Address.DoesNotExist, 'MultipleObjectsReturned': address.models.Address.MultipleObjectsReturned, 'zipcode': <django.db.models.query_utils.DeferredAttribute at 0x106706970>, 'prefecture_id': <django.db.models.fields.related_descriptors.ForeignKeyDeferredAttribute at 0x1068b1040>, 'prefecture': <django.db.models.fields.related_descriptors.ForwardManyToOneDescriptor at 0x1068b10a0>, 'name': <django.db.models.query_utils.DeferredAttribute at 0x1068b1190>, 'memo': <django.db.models.query_utils.DeferredAttribute at 0x1068b11f0>, 'id': <django.db.models.query_utils.DeferredAttribute at 0x1068b1310>, 'objects': <django.db.models.manager.ManagerDescriptor at 0x1068b1250>}) ϞσϧΫϥεͷ __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)
  94. >>> del address.zipcode >>> address.zipcode '2480012' ← delͨ͠͸ͣͳͷʹɺΞΫηεͨ͠Β஋͕໭͖ͬͯͨ ϙΠϯτ Djangoͷॅॴ࿥Ϟσϧ

    >>> address.__dict__ {'_state': <django.db.models.base.ModelState at 0x107956220>, 'id': 1, 'zipcode': '2480012', 'prefecture_id': 14, 'name': 'ח૔ࢢ໾ॴ', 'memo': ''}
  95. • @everes follow me on twitter • גࣜձࣾσΟʔɾΤψɾΤʔ γεςϜຊ෦CTOࣨ •

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

    ύʔϑΣΫτPythonʦվగ2൛ʧग़ͯ·͢ ͓·͑୭Αʁ πϢΩϚίτ https://amzn.to/2zaJIiV ͑͏ʃ͊͞Μ ͬͯݺΜͰͶʂ