김성현
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