Seonghyeon Kim
Newbie developer
김성현
NovemberOscar sierrasevn
https://seonghyeon.dev
{ self@seonghyeon.dev | k.seonghyeon@mymusictaste.com }
>>> v = object()
>>> id(v)
4473343200
>>> v = "hello"
>>> x = v
>>> id(x) == id(v)
True
>>> x += "o"
>>> id(x) == id(v)
False
>>> v = "hello"
>>> type(v)
<class 'str'>
>>> v = 42
>>> type(v)
<class 'int'>
class
객체는 파이썬이 데이터를 추상화한 것입니다. 파이썬 프로그램의 모든 데이터는 객체나 객체 간의 관계로 표현됩니다. 폰 노이만의 "프로그램 내장식 컴퓨터" 모델을 따르고, 또 그 관점에서 코드 역시 객체로 표현됩니다.
>>> class C:
... pass
>>> id(C)
140559438115784
>>> dir(C)
[ ... ]
>>> type(C)
<class 'type'>
>>> type(C)
<class 'type'>
type()
>>> class X:
... a = 1
>>> X = type('X', (object,), dict(a=1))
>>> X
<class '__main__.X'>
class MyClass:
z = 1
def f(self,):
return x
from collections import OrederedDict
class MyMeta(type):
@classmethod
def __prepare__(mcs, name, bases, **kwargs):
return OrderedDict()
class MyClass(metaclass=MyMeta):
z = 1
def f(self,):
return x
print({k: v for k, v in MyClass.__dict__.items() if not k.startswith("__")})
# {'z': 1, 'f': <function MyClass.f at 0x10efc8bf8>}
class MyMeta(type):
def __new__(mcs, *args, **kwargs):
print(f"new class: {args}")
r = super().__new__(mcs, *args, **kwargs)
return r
class MyClass(metaclass=MyMeta):
z = 1
def f(self, x):
return x
# new class: ('MyClass', (), {'__module__': '__main__', '__qualname__': 'MyClass', 'z': 1, 'f': <function MyClass.f at 0x101cc89d8>})
# same as type('MyClass', (), {...})
class MyMeta(type):
def __call__(cls, *args, **kwargs):
x = super().__call__(*args, **kwargs)
print(f"A new object created: {x}")
return x
class MyClass(metaclass=MyMeta):
z = 1
def f(self, x):
return x
m = MyClass()
print(f"created object is {m}")
# A new object created: <__main__.MyClass object at 0x1073d54e0>
# created object is <__main__.MyClass object at 0x1073d54e0>
class DisallowMultipleInheritance(type):
def __new__(mcs, *args, **kwargs):
if len(args[1]) > 1:
raise Exception("...")
new_cls = super().__new__(mcs, *args, **kwargs)
return new_cls
class Foo(metaclass=DisallowMultipleInheritance):
pass
class Bar:
pass
class Zee(Foo, Bar):
pass
Traceback (most recent call last):
File "/.../disallow_multiple_inheritance.py", line 17, in <module>
class Zee(Foo, Bar):
File "/.../disallow_multiple_inheritance.py", line 4, in __new__
raise Exception(f"Can't be subclassed with multiple inheritance: {args[1]}")
Exception: Can't be subclassed with multiple inheritance: (<Foo>, <Bar>)
class DisallowInheritance(type):
def __new__(mcs, *args, **kwargs):
cls = [c for c in args[1] if isinstance(c, mcs)]
if cls:
raise Exception(f"can't subclass {cls}")
r = super().__new__(mcs, *args, **kwargs)
return r
class UserForm(ModelForm):
class Meta:
model = User
fields = ['name', 'email', 'birth_date']
class CheckMeta(type):
def __new__(mcs, *args, **kwargs):
name, bases, namespace = args
if (not namespace.get("Meta", None)) and (bases != ()):
raise Exception("Can not configure class. Meta is missing")
r = super().__new__(mcs, *args, **kwargs)
return r
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(...)
return cls._instances[cls]
class C(metaclass=Singleton):
pass
>>> a = C()
>>> b = C()
>>> print(a is b)
True
(Python 3.6부터는 자체 지원)
(Python 3.6부터는 자체 지원)
class Product:
price = Price(name="price", unit="KRW")
(Python 3.6부터는 자체 지원)
>>> pd = Product()
>>> pd.price = "NotANumber"
Please set a valid integer
>>> pd.price = 5000
>>> print(pd.price)
5,000 KRW
(Python 3.6부터는 자체 지원)
(Python 3.6부터는 자체 지원)
(Python 3.6부터는 자체 지원)
(Python 3.6부터는 자체 지원)
class Meta(type):
def __new__(mcs, *args, **kwargs):
name, bases, namespace = args
self = super().__new__(mcs, name, bases, namespace)
for k, v in self.__dict__.items():
func = getattr(v, '__set_name__', None)
if func is not None: # trigger hook if exists
func(self, k)
return self
(Python 3.6부터는 자체 지원)
class Desc:
def __get__(self, instance, owner):
return instance.__dict__[self.name]
def __set__(self, instance, value):
instance.__dict__[self.name] = value
def __set_name__(self, owner, name):
self.name = name
(Python 3.6부터는 자체 지원)
class C(metaclass=Meta):
v = Desc()
(Python 3.6부터는 자체 지원)
>>> c = C()
>>> c.v = 3
>>> print(c.v == 3)
True
class TaskType(type):
"""Meta class for tasks.
Automatically registers the task in the task registry, except
if the `abstract` attribute is set.
If no `name` attribute is provided, then no name is automatically
set to the name of the module it was defined in, and the class name.
"""
def __new__(cls, name, bases, attrs):
new = super(TaskType, cls).__new__
task_module = attrs.get("__module__") or "__main__"
# - Abstract class: abstract attribute should not be inherited.
if attrs.pop("abstract", None) or not attrs.get("autoregister", True):
return new(cls, name, bases, attrs)
# The 'app' attribute is now a property, with the real app located
# in the '_app' attribute. Previously this was a regular attribute,
# so we should support classes defining it.
_app1, _app2 = attrs.pop("_app", None), attrs.pop("app", None)
app = attrs["_app"] = _app1 or _app2 or current_app
# - Automatically generate missing/empty name.
autoname = False
if not attrs.get("name"):
try:
module_name = sys.modules[task_module].__name__
except KeyError: # pragma: no cover
# Fix for manage.py shell_plus (Issue #366).
module_name = task_module
attrs["name"] = '.'.join(filter(None, [module_name, name]))
autoname = True
# - Create and register class.
# Because of the way import happens (recursively)
# we may or may not be the first time the task tries to register
# with the framework. There should only be one class for each task
# name, so we always return the registered version.
tasks = app._tasks
# - If the task module is used as the __main__ script
# - we need to rewrite the module part of the task name
# - to match App.main.
if MP_MAIN_FILE and sys.modules[task_module].__file__ == MP_MAIN_FILE:
# - see comment about :envvar:`MP_MAIN_FILE` above.
task_module = "__main__"
if autoname and task_module == "__main__" and app.main:
attrs["name"] = '.'.join([app.main, name])
task_name = attrs["name"]
if task_name not in tasks:
tasks.register(new(cls, name, bases, attrs))
instance = tasks[task_name]
instance.bind(app)
return instance.__class__
This is simplified form. not original
class TaskType(type):
def __new__(cls, name, bases, attrs):
# get __new__ form super
# get app from attrs. if not exists, get from current_app
# automatically generate missing/empty name.
...
tasks = app._tasks
...
task_name = attrs["name"]
if task_name not in tasks:
tasks.register(new(cls, name, bases, attrs))
instance = tasks[task_name]
instance.bind(app)
return instance.__class__
class Form(BaseForm, metaclass=DeclarativeFieldsMetaclass):
"A collection of Fields, plus their associated data."
# This is a separate class from BaseForm in order to abstract the way
# self.fields is specified. This class (Form) is the one that does the
# fancy metaclass stuff purely for the semantic sugar -- it allows one
# to define a form using declarative syntax.
# BaseForm itself has no way of designating self.fields.
class DeclarativeFieldsMetaclass(MediaDefiningClass):
"""Collect Fields declared on the base classes."""
def __new__(mcs, name, bases, attrs):
# Collect fields from current class.
current_fields = []
for key, value in list(attrs.items()):
if isinstance(value, Field):
current_fields.append((key, value))
attrs.pop(key)
attrs['declared_fields'] = dict(current_fields)
new_class = super(DeclarativeFieldsMetaclass, mcs).__new__(mcs, name, bases, attrs)
# Walk through the MRO.
declared_fields = {}
for base in reversed(new_class.__mro__):
# Collect fields from base class.
if hasattr(base, 'declared_fields'):
declared_fields.update(base.declared_fields)
# Field shadowing.
for attr, value in base.__dict__.items():
if value is None and attr in declared_fields:
declared_fields.pop(attr)
new_class.base_fields = declared_fields
new_class.declared_fields = declared_fields
return new_class
class DeclarativeFieldsMetaclass(MediaDefiningClass):
"""Collect Fields declared on the base classes."""
def __new__(mcs, name, bases, attrs):
# Collect fields from current class.
current_fields = []
for key, value in list(attrs.items()):
if isinstance(value, Field):
current_fields.append((key, value))
attrs.pop(key)
attrs['declared_fields'] = dict(current_fields)
new_class = super(DeclarativeFieldsMetaclass, mcs).__new__(mcs, name, bases, attrs)
# Walk through the MRO.
declared_fields = {}
...
new_class.base_fields = declared_fields
new_class.declared_fields = declared_fields
return new_class
class ModelFormMetaclass(DeclarativeFieldsMetaclass):
def __new__(mcs, name, bases, attrs):
base_formfield_callback = None
for b in bases:
if hasattr(b, 'Meta') and hasattr(b.Meta, 'formfield_callback'):
base_formfield_callback = b.Meta.formfield_callback
break
formfield_callback = attrs.pop('formfield_callback', base_formfield_callback)
new_class = super(ModelFormMetaclass, mcs).__new__(mcs, name, bases, attrs)
if bases == (BaseModelForm,):
return new_class
opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None))
...
class ModelFormMetaclass(DeclarativeFieldsMetaclass):
def __new__(mcs, name, bases, attrs):
...
# We check if a string was passed to `fields` or `exclude`,
# which is likely to be a mistake where the user typed ('foo') instead
# of ('foo',)
for opt in ['fields', 'exclude', 'localized_fields']:
value = getattr(opts, opt)
if isinstance(value, str) and value != ALL_FIELDS:
msg = ("%(model)s.Meta.%(opt)s cannot be a string. "
"Did you mean to type: ('%(value)s',)?" % {
'model': new_class.__name__,
'opt': opt,
'value': value,
})
raise TypeError(msg)
...
class ModelFormMetaclass(DeclarativeFieldsMetaclass):
def __new__(mcs, name, bases, attrs):
...
if opts.model:
# If a model is defined, extract form fields from it.
# make sure opts.fields doesn't specify an invalid field
# Override default model fields with any custom declared ones
# (plus, include all the other declared fields).
...
fields.update(new_class.declared_fields)
else:
fields = new_class.declared_fields
new_class.base_fields = fields
return new_class
async def qna_time🙏(questions):
answer = await asyncio.gather(*questions)
return answer
By Seonghyeon Kim
파이콘 한국 2019의 "리얼월드 메타클래스" 세션의 슬라이드입니다. *MacOS에서 가장 잘 보입니다.