Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save spinfish/8ebb32dd2ca593c7e8e71736872965ef to your computer and use it in GitHub Desktop.
Save spinfish/8ebb32dd2ca593c7e8e71736872965ef to your computer and use it in GitHub Desktop.
On this day, 03/01/2020, I think I may have finally understood how metaclasses work. At least I think so. Here are some silly ones I made on that day to test out my new found knowledge.
from types import MappingProxyType as _Mapping
class _DescriptorDunderMeta(type):
"""
A metaclass that allows us to access all the builtin descriptors
used in the class (:class:`property`, :class:`classmethod`,
:class:`staticmethod`) collected into 3 groups of dunder attributes.
"""
def __new__(metacls, name, bases, attrs):
types = ("properties", "classmethods", "staticmethods")
for variable in types:
globals().update({variable: {}}) # Disgusting, I know
new_cls = super().__new__(metacls, name, bases, attrs)
for key, value in new_cls.__dict__.items():
if key.startswith("_"):
continue
if isinstance(value, classmethod):
classmethods[key] = value.__func__
elif isinstance(value, property):
properties[key] = _Mapping({
attr: getattr(value, attr)
for attr in dir(value)
if not attr.startswith("_")
})
elif isinstance(value, staticmethod):
staticmethods[key] = value.__func__
def _property_maker(value):
return property(lambda _: value)
for prop in types: # Gross on purpose... because I can
setattr(
new_cls,
"__{}__".format(prop),
_property_maker(_Mapping(globals()[prop]))
)
return new_cls
class MyClass(metaclass=_DescriptorDunderMeta):
"""
A class to showcase the grouping of builtin descriptors that
our metaclass does via an immutable set of dunder properties.
"""
proppy = property(lambda self: "Hi, I am the {!r} property!".format(self))
classmeth = classmethod(lambda cls: "Hi, I am the {!r} classmethod!".format(cls))
staticmeth = staticmethod(lambda: "Hi, I am the special staticmethod!")
instance = MyClass()
print(instance.__dict__)
print(instance.__properties__["proppy"]["fget"](instance))
print(instance.__classmethods__["classmeth"](instance.__class__))
print(instance.__staticmethods__["staticmeth"]())
class _list(list):
"""Subclass of :class:`list` to allow our metaclass to work."""
remove = lambda self, item: super().remove(item) or self
class _NoPrivateOverrideMeta(type):
"""A metaclass that disallows subclasses to override "private" methods."""
def __new__(metacls, name, bases, attrs):
def __init_subclass(cls, **kwargs):
for key, val in cls.__dict__.items():
if not key.endswith("__") and key[0] == "_" and (any(
key in member for member in map(
lambda member: member.__dict__,
_list(cls.__mro__).remove(cls)
)
)):
raise TypeError(f"Class {cls!r} must not have overridden private method {val!r}")
super().__init_subclass__(**kwargs)
attrs["__init_subclass__"] = __init_subclass
return super().__new__(metacls, name, bases, attrs)
class NoPrivateOverride(metaclass=_NoPrivateOverrideMeta):
"""Base class containg a single private method."""
def _private_method(self):
print("I am a private method that should not attempted to be overridden!")
class DerivedWithNoErrors(NoPrivateOverride):
"""
An example of a valid subclass of :class:`NoPrivateOverride`
with no overridden private methods.
"""
def _another_private_method(self):
print("I will exist with no errors whatsoever!")
class DerivedWithError(NoPrivateOverride):
"""An example of an invalid subclass with its private method overridden."""
def _private_method(self):
print("You will never see me because I will never get executed!")
# Think of type.__new__ like this:
# type.__new__(type, "MyClass", (), {})
# All classes are instances of `type` by default,
# Meaning isinstance(MyClass, type) is True
# Class instances are instances of `object`
# Meaning isinstance(MyClass(), object) is True
# WARNING: Most of these are really dumb examples trying to teach how a metaclass works
# Reading this post really helped me with understanding them, however it took some playing
# Around before I understood them fully:
# https://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/
# Good luck!
################################################################################################
################################################################################################
class _SlotsMeta(type):
"""A metaclass that allows us to set ``__slots__`` at class definition."""
def __new__(metacls, *args, slots=None):
new_cls = super().__new__(metacls, *args)
if slots is None:
return new_cls
if not isinstance(slots, (list, tuple)):
raise TypeError(
"slots must be an instance of list/tuple not {!r}".format(
slots.__class__
))
new_cls.__slots__ = slots
return new_cls
class ShowSlotsMeta(metaclass=_SlotsMeta, slots=("attr",)):
"""To showcase our slots metaclass."""
instance = ShowSlotsMeta()
print(hasattr(instance, "__slots__"))
try:
instance.non_slot_attr = "test"
except AttributeError as error:
print("Encountered {!r}".format(error))
instance.attr = "test"
################################################################################################
################################################################################################
class _PropertyMeta(type):
"""
A metaclass that allows us to set basic properties at class definition
(i.e. ones without setter and deleter methods and return a constant value).
Note that these overwrite the properties in the class body.
"""
def __new__(cls, *args, **properties):
new_cls = super().__new__(cls, *args)
if not properties:
return new_cls
# Thanks to SebbyLaw for teaching me about what pass by reference and pass by value mean
# i.e. why I can't do setattr(new_cls, property_name, property(lambda self: value))
def _dummy(value):
return property(lambda self: value)
for property_name, value in properties.items():
setattr(new_cls, property_name, _dummy(value))
return new_cls
class ShowPropertyMeta(metaclass=_PropertyMeta, prop="test one", another_prop="test two"):
"""To showcase our properties metaclass."""
instance = ShowPropertyMeta()
print(hasattr(instance, "prop"), hasattr(instance, "another_prop"))
print(instance.prop, instance.another_prop, sep="\n")
################################################################################################
################################################################################################
class _OverrideMeta(type):
"""
A very simple metaclass that allows us to overwrite specific methods
defined in a class body at class definition.
"""
def __new__(metacls, name, bases, attrs, **overrides):
attrs.update(overrides)
return super().__new__(metacls, name, bases, attrs)
class ShowOverride(metaclass=_OverrideMeta, __init__=(lambda self: print("You'll see me instead."))):
"""To showcase our override metaclass."""
def __init__(self):
print("You will not see me!")
ShowOverride()
################################################################################################
################################################################################################
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment