Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

makoto tsuyuki

August 29, 2020
Tweet

More Decks by makoto tsuyuki

Other Decks in Programming

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

    View Slide

  2. 2005೥

    View Slide

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

    View Slide

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

    View Slide

  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__

    View Slide

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

    View Slide

  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__ ϝιου͕ఆٛ͞Ε͍ͯΔ
    ϝλΫϥε

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    γʔτ = ද ʹςʔϒϧ

    View Slide

  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

    View Slide

  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

    View Slide

  21. 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Ͱ͸ͳ͘ͳͬͯΔ
    ϙΠϯτ

    View Slide

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

    View Slide

  23. 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=‘ח૔ࢢ໾ॴ')

    View Slide

  24. 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=‘ח૔ࢢ໾ॴ')

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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': ,
    '__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

    View Slide

  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': ,
    '__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

    View Slide

  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

    View Slide

  32. 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

    View Slide

  33. ʢଐੑʣ
    ATTRIBUTE

    View Slide

  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
    Πϯελϯεଐੑ

    View Slide

  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ͰΞΫηε͢Δݟ׳Εͨ΍ͭ

    View Slide

  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 Έ͍ͨʹΠϯελϯεܦ༝ͰΞΫηεͰ͖Δ

    View Slide

  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__': ,
    '__weakref__': ,
    '__doc__': None})
    Ϋϥεଐੑ

    View Slide

  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__': ,
    '__weakref__': ,
    '__doc__': None})
    Ϋϥεଐੑ
    ← Ϋϥεʹॻ͍ͯ͋Δ΍ͭ

    View Slide

  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__': ,
    '__weakref__': ,
    '__doc__': None})
    Ϋϥεଐੑ

    View Slide

  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__': ,
    '__weakref__': ,
    '__doc__': None})
    Ϋϥεଐੑ
    ← Πϯελϯεͷ __dict__ ʹ͸ݟ౰ͨΒͳ͍

    View Slide

  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__': ,
    '__weakref__': ,
    '__doc__': None})
    Ϋϥεଐੑ
    ← Πϯελϯεͷ __dict__ ʹ͸ݟ౰ͨΒͳ͍
    ← Πϯελϯεܦ༝Ͱ΋ΞΫηε͸Ͱ͖Δ

    View Slide

  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__': ,
    '__weakref__': ,
    '__doc__': None})
    Ϋϥεଐੑ
    ← Πϯελϯεͷ __dict__ ʹ͸ݟ౰ͨΒͳ͍
    ← Πϯελϯεܦ༝Ͱ΋ΞΫηε͸Ͱ͖Δ
    ← ಉ໊ͷΠϯελϯεଐੑΛׂΓ౰ͯΔͱɺΠϯελϯεͷ __dict__ ʹݱΕΔ

    View Slide

  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__': ,
    '__weakref__': ,
    '__doc__': None})
    Ϋϥεଐੑ
    ← Πϯελϯεͷ __dict__ ʹ͸ݟ౰ͨΒͳ͍
    ← Πϯελϯεܦ༝Ͱ΋ΞΫηε͸Ͱ͖Δ
    ← ಉ໊ͷΠϯελϯεଐੑΛׂΓ౰ͯΔͱɺΠϯελϯεͷ __dict__ ʹݱΕΔ
    ← Ϋϥεͷ __dict__ ͷ஋͸ݩͷ··

    View Slide

  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': ,
    '__dict__': ,
    '__weakref__': ,
    '__doc__': None})
    Ϋϥεϝιου

    View Slide

  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'}
    Ϋϥεϝιου

    View Slide

  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__ ʹ͸ݟ౰ͨΒͳ͍

    View Slide

  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’ ΛׂΓ౰ͯ

    View Slide

  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’ ΛׂΓ౰ͯ
    ← Ϋϥεϝιουܦ༝͸ɺΫϥεଐੑΛࢀর͢Δ

    View Slide

  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’ ΛׂΓ౰ͯ
    ← Ϋϥεϝιουܦ༝͸ɺΫϥεଐੑΛࢀর͢Δ
    ← Πϯελϯεଐੑ͸ΫϥεଐੑΛࢀরͯ͠΋มΘΒͳ͍

    View Slide

  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'}
    ϓϩύςΟʢෆཁͳΞΫηα͸࡞Βͳ͍ʣ

    View Slide

  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͡Όͳ͍

    View Slide

  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'
    ΧϓηϧԽͱ͔͋ͱ͔ΒͰ͖ΔΜͰ
    ඞཁʹͳΔ·Ͱແବͳίʔυ͸ॻ͔ͳ͍

    View Slide

  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'

    View Slide

  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͕ಈ࡞͍ͯ͠Δ

    View Slide

  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'}

    View Slide

  56. ʢΫϥεܧঝʣ
    CLASSES

    View Slide

  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
    ܧঝ

    View Slide

  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
    ܧঝ
    ϝιου୳ࡧॱ

    View Slide

  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
    ܧঝ
    ϝιου୳ࡧॱ

    View Slide

  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
    ܧঝ
    ϝιου୳ࡧॱ

    View Slide

  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 ϝιου୳ࡧॱ

    View Slide

  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)
    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

    View Slide

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

    View Slide

  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)
    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

    View Slide

  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)
    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}

    View Slide

  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

    View Slide

  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

    View Slide

  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)

    View Slide

  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

    View Slide

  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

    View Slide

  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”)


    View Slide

  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ͰରԠ͢Δ໊લΛઃఆ͢Δ

    View Slide

  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ͰରԠ͢Δ໊લΛઃఆ͢Δ
    ←ׂΓ౰ͯΒΕͨม਺໊

    View Slide

  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': ,

    >>> spam.__dict__
    {'ham': }
    >>> spam.ham

    >>> spam.ham = 'bacon'
    >>> spam.__dict__
    {'ham': ‘bacon'}
    >>> spam.ham
    ‘bacon'

    View Slide

  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': ,

    >>> spam.__dict__
    {'ham': }
    >>> spam.ham

    >>> spam.ham = 'bacon'
    >>> spam.__dict__
    {'ham': ‘bacon'}
    >>> spam.ham
    ‘bacon'
    ← Πϯελϯεͷଐੑʹ

    View Slide

  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': ,

    >>> 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__

    View Slide

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

    View Slide

  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': ,
    '__dict__': ,
    '__weakref__': ,
    '__doc__': None})
    >>> spam.__dict__
    {}
    >>> spam.egg

    >>> spam.egg = 'ham'
    SetNothingDescriptor.__set__
    >>> spam.egg

    >>> spam.__dict__['egg'] = 'ham'
    >>> spam.egg
    'ham'
    >>> spam.egg = 'ham'
    SetNothingDescriptor.__set__
    __set__ / __delete__ ͷ͍ͣΕ͔͕͋Δ৔߹͸
    σʔλσΟεΫϦϓλ(data descriptor)

    View Slide

  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)

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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͔Β)

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  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)
    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

    View Slide

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

    View Slide

  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

    View Slide

  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)
    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

    View Slide

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

    View Slide

  93. 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)

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide