diff --git a/python-metaclasses/README.md b/python-metaclasses/README.md new file mode 100644 index 0000000000..dcb91a5094 --- /dev/null +++ b/python-metaclasses/README.md @@ -0,0 +1,11 @@ +# Python Metaclasses + +This folder provides the code examples for the Real Python tutorial [Python Metaclasses](https://realpython.com/python-metaclasses/). + +The tutorial demonstrates its examples in the interactive interpreter, so each +REPL sequence is collected here as a runnable script that prints the output the +tutorial shows. Run any of them with Python 3.14: + +```sh +$ python custom_metaclass.py +``` diff --git a/python-metaclasses/class_decorator.py b/python-metaclasses/class_decorator.py new file mode 100644 index 0000000000..6b2e8e890f --- /dev/null +++ b/python-metaclasses/class_decorator.py @@ -0,0 +1,25 @@ +def decorator(cls): + class NewClass(cls): + attr = 100 + + return NewClass + + +@decorator +class X: + pass + + +@decorator +class Y: + pass + + +@decorator +class Z: + pass + + +print(X.attr) +print(Y.attr) +print(Z.attr) diff --git a/python-metaclasses/class_factory.py b/python-metaclasses/class_factory.py new file mode 100644 index 0000000000..bf9a397c0d --- /dev/null +++ b/python-metaclasses/class_factory.py @@ -0,0 +1,24 @@ +class Meta(type): + def __init__(cls, name, bases, dct): + cls.attr = 100 + + +class X(metaclass=Meta): + pass + + +print(X.attr) + + +class Y(metaclass=Meta): + pass + + +print(Y.attr) + + +class Z(metaclass=Meta): + pass + + +print(Z.attr) diff --git a/python-metaclasses/custom_metaclass.py b/python-metaclasses/custom_metaclass.py new file mode 100644 index 0000000000..2876be3d00 --- /dev/null +++ b/python-metaclasses/custom_metaclass.py @@ -0,0 +1,23 @@ +class Meta(type): + def __new__(cls, name, bases, dct): + x = super().__new__(cls, name, bases, dct) + x.attr = 100 + return x + + +class Foo(metaclass=Meta): + pass + + +print(Foo.attr) + + +class Bar(metaclass=Meta): + pass + + +class Qux(metaclass=Meta): + pass + + +print((Bar.attr, Qux.attr)) diff --git a/python-metaclasses/custom_new_method.py b/python-metaclasses/custom_new_method.py new file mode 100644 index 0000000000..135fe27167 --- /dev/null +++ b/python-metaclasses/custom_new_method.py @@ -0,0 +1,17 @@ +class Foo: + pass + + +def new(cls): + x = object.__new__(cls) + x.attr = 100 + return x + + +Foo.__new__ = new + +f = Foo() +print(f.attr) + +g = Foo() +print(g.attr) diff --git a/python-metaclasses/dynamic_class_example_1.py b/python-metaclasses/dynamic_class_example_1.py new file mode 100644 index 0000000000..00b85b974c --- /dev/null +++ b/python-metaclasses/dynamic_class_example_1.py @@ -0,0 +1,14 @@ +# Defining the class dynamically with type() +Foo = type("Foo", (), {}) + +x = Foo() +print(x) + + +# The same class defined the usual way, with the class statement +class Foo: + pass + + +x = Foo() +print(x) diff --git a/python-metaclasses/dynamic_class_example_2.py b/python-metaclasses/dynamic_class_example_2.py new file mode 100644 index 0000000000..445054da32 --- /dev/null +++ b/python-metaclasses/dynamic_class_example_2.py @@ -0,0 +1,21 @@ +# Foo comes from Example 1 +Foo = type("Foo", (), {}) + +# Defining the class dynamically with type() +Bar = type("Bar", (Foo,), dict(attr=100)) + +x = Bar() +print(x.attr) +print(x.__class__) +print(x.__class__.__bases__) + + +# The same class defined the usual way, with the class statement +class Bar(Foo): + attr = 100 + + +x = Bar() +print(x.attr) +print(x.__class__) +print(x.__class__.__bases__) diff --git a/python-metaclasses/dynamic_class_example_3.py b/python-metaclasses/dynamic_class_example_3.py new file mode 100644 index 0000000000..48bd66c972 --- /dev/null +++ b/python-metaclasses/dynamic_class_example_3.py @@ -0,0 +1,19 @@ +# Defining the class dynamically with type() +Foo = type("Foo", (), {"attr": 100, "attr_val": lambda x: x.attr}) + +x = Foo() +print(x.attr) +print(x.attr_val()) + + +# The same class defined the usual way, with the class statement +class Foo: + attr = 100 + + def attr_val(self): + return self.attr + + +x = Foo() +print(x.attr) +print(x.attr_val()) diff --git a/python-metaclasses/dynamic_class_example_4.py b/python-metaclasses/dynamic_class_example_4.py new file mode 100644 index 0000000000..26ee24e12b --- /dev/null +++ b/python-metaclasses/dynamic_class_example_4.py @@ -0,0 +1,21 @@ +def f(obj): + print("attr =", obj.attr) + + +# Defining the class dynamically with type() +Foo = type("Foo", (), {"attr": 100, "attr_val": f}) + +x = Foo() +print(x.attr) +x.attr_val() + + +# The same class defined the usual way, with the class statement +class Foo: + attr = 100 + attr_val = f + + +x = Foo() +print(x.attr) +x.attr_val() diff --git a/python-metaclasses/new_style_classes.py b/python-metaclasses/new_style_classes.py new file mode 100644 index 0000000000..451c3b20aa --- /dev/null +++ b/python-metaclasses/new_style_classes.py @@ -0,0 +1,15 @@ +class Foo: + pass + + +obj = Foo() +print(obj.__class__) +print(type(obj)) +print(obj.__class__ is type(obj)) + +n = 5 +d = {"x": 1, "y": 2} +x = Foo() + +for obj in (n, d, x): + print(type(obj) is obj.__class__) diff --git a/python-metaclasses/object_factory.py b/python-metaclasses/object_factory.py new file mode 100644 index 0000000000..87f8ea8f0f --- /dev/null +++ b/python-metaclasses/object_factory.py @@ -0,0 +1,13 @@ +class Foo: + def __init__(self): + self.attr = 100 + + +x = Foo() +print(x.attr) + +y = Foo() +print(y.attr) + +z = Foo() +print(z.attr) diff --git a/python-metaclasses/reassigning_type_new.py b/python-metaclasses/reassigning_type_new.py new file mode 100644 index 0000000000..177a642240 --- /dev/null +++ b/python-metaclasses/reassigning_type_new.py @@ -0,0 +1,11 @@ +# Spoiler alert: This doesn't work! +def new(cls): + x = type.__new__(cls) + x.attr = 100 + return x + + +try: + type.__new__ = new +except TypeError as ex: + print(ex) diff --git a/python-metaclasses/simple_inheritance.py b/python-metaclasses/simple_inheritance.py new file mode 100644 index 0000000000..935556316c --- /dev/null +++ b/python-metaclasses/simple_inheritance.py @@ -0,0 +1,19 @@ +class Base: + attr = 100 + + +class X(Base): + pass + + +class Y(Base): + pass + + +class Z(Base): + pass + + +print(X.attr) +print(Y.attr) +print(Z.attr) diff --git a/python-metaclasses/type_and_class.py b/python-metaclasses/type_and_class.py new file mode 100644 index 0000000000..0cc2fea73c --- /dev/null +++ b/python-metaclasses/type_and_class.py @@ -0,0 +1,12 @@ +class Foo: + pass + + +x = Foo() +print(type(x)) +print(type(Foo)) + +for t in int, float, dict, list, tuple: + print(type(t)) + +print(type(type)) diff --git a/python-metaclasses/type_with_one_argument.py b/python-metaclasses/type_with_one_argument.py new file mode 100644 index 0000000000..602a7d09c6 --- /dev/null +++ b/python-metaclasses/type_with_one_argument.py @@ -0,0 +1,12 @@ +print(type(3)) +print(type(["foo", "bar", "baz"])) + +t = (1, 2, 3, 4, 5) +print(type(t)) + + +class Foo: + pass + + +print(type(Foo()))