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 full-size slide

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

    View full-size slide

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

    View full-size 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
    __init__
    __dict__

    View full-size slide

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

    View full-size slide

  6. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    γʔτ = ද ʹςʔϒϧ

    View full-size slide

  18. 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 full-size 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)
    ϞσϧΫϥε
    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 full-size slide

  20. 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 full-size slide

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

    View full-size slide

  22. 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 full-size slide

  23. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  28. 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 full-size slide

  29. 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 full-size slide

  30. 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 full-size slide

  31. 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 full-size slide

  32. ʢଐੑʣ
    ATTRIBUTE

    View full-size slide

  33. 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 full-size 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
    Πϯελϯεଐੑ
    ← selfͰΞΫηε͢Δݟ׳Εͨ΍ͭ

    View full-size 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ͰΞΫηε͢Δݟ׳Εͨ΍ͭ
    ← spam.egg Έ͍ͨʹΠϯελϯεܦ༝ͰΞΫηεͰ͖Δ

    View full-size slide

  36. 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 full-size 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 full-size slide

  38. 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 full-size 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})
    Ϋϥεଐੑ
    ← Πϯελϯεͷ __dict__ ʹ͸ݟ౰ͨΒͳ͍

    View full-size 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 full-size 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__ ʹ͸ݟ౰ͨΒͳ͍
    ← Πϯελϯεܦ༝Ͱ΋ΞΫηε͸Ͱ͖Δ
    ← ಉ໊ͷΠϯελϯεଐੑΛׂΓ౰ͯΔͱɺΠϯελϯεͷ __dict__ ʹݱΕΔ

    View full-size 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__ ʹݱΕΔ
    ← Ϋϥεͷ __dict__ ͷ஋͸ݩͷ··

    View full-size slide

  43. 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 full-size slide

  44. 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 full-size 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'}
    Ϋϥεϝιου
    ← Πϯελϯεͷ __dict__ ʹ͸ݟ౰ͨΒͳ͍

    View full-size 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__ ʹ͸ݟ౰ͨΒͳ͍
    ← Πϯελϯεଐੑʹ ‘ham’ ΛׂΓ౰ͯ

    View full-size 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 full-size 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 full-size slide

  49. 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 full-size 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'}
    ϓϩύςΟʢෆཁͳΞΫηα͸࡞Βͳ͍ʣ
    ಈ࡞͢Δ͚ΕͲμϝ
    PYTHONIC͡Όͳ͍

    View full-size slide

  51. 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 full-size slide

  52. 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 full-size 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'
    ← ͜ͷ࣌఺Ͱ@egg.setter͕ಈ࡞͍ͯ͠Δ

    View full-size slide

  54. 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 full-size slide

  55. ʢΫϥεܧঝʣ
    CLASSES

    View full-size slide

  56. 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 full-size 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 full-size slide

  58. 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 full-size slide

  59. 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 full-size slide

  60. 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 full-size slide

  61. 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 full-size slide

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

    View full-size slide

  63. 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 full-size 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 = -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 full-size slide

  65. 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 full-size slide

  66. 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 full-size slide

  67. 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 full-size 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()
    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 full-size slide

  69. Πϯελϯε࡞Δͱ͖ʹΞαΠϯ͢Δͷͱಉ໊͡લΛ͚ͭΔ
    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 full-size 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
    value1 = PositiveInteger(“value1”)


    View full-size slide

  71. 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 full-size 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 full-size slide

  73. 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'

    View full-size 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': <__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'
    ← Πϯελϯεͷଐੑʹ

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  78. 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 full-size slide

  79. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  83. μϯμʔ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 full-size slide

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

    View full-size slide

  85. 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 full-size slide

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

    View full-size slide

  87. 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 full-size slide

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

    View full-size slide

  89. 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 full-size slide

  90. 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 full-size slide

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

    View full-size slide

  92. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide