Skip to content

Instantly share code, notes, and snippets.

@depau
Last active July 27, 2023 16:16
Show Gist options
  • Save depau/8920287057a1ff5723d9bffe25f6662a to your computer and use it in GitHub Desktop.
Save depau/8920287057a1ff5723d9bffe25f6662a to your computer and use it in GitHub Desktop.
Python initialization adventures

Recommended viewing experience

Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
},
"tags": []
},
"source": [
"# Python initialization adventures\n",
"\n",
"Yo I heard you like to create classes. Let me blow your mind a little bit."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"class: <class '__main__.MyClass'>\n",
"Greetings from the constructor\n",
"instance: <__main__.MyClass object at 0x7fab98355890>\n",
"I'm a method! cucumber\n"
]
}
],
"source": [
"class MyClass:\n",
" class_attribute = \"lol\"\n",
" \n",
" def __init__(self, value):\n",
" print(\"Greetings from the constructor\")\n",
" self.instance_attribute = value\n",
" \n",
" def method(self):\n",
" print(f\"I'm a method! {self.instance_attribute}\")\n",
"\n",
"print(\"class:\", MyClass)\n",
"\n",
"i = MyClass(\"cucumber\")\n",
"print(\"instance:\", i)\n",
"\n",
"i.method()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"---\n",
"But what if we want things to be a little cursed?"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"I'm a method! cucumber\n"
]
}
],
"source": [
"MyClass.method(i)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"I'm a method! celery\n"
]
}
],
"source": [
"class EmptyClass:\n",
" pass\n",
"\n",
"obj = EmptyClass()\n",
"obj.instance_attribute = \"celery\"\n",
"\n",
"MyClass.method(obj)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Well, that's fair enough I guess?\n",
"\n",
"It's a bit weird, though, isn't it?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Class construction\n",
"\n",
"Let's bring up some self-hatred and attempt to construct a class by hand. The keyword `class` is not allowed!\n",
"\n",
"What do we need to do? What is a class?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Everything is an object\n",
"\n",
"Including a class! Let's check"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"MyClass extends object? True\n",
"instance extends object? True\n",
"method extends object? True\n"
]
}
],
"source": [
"print(\"MyClass extends object? \", isinstance(MyClass, object))\n",
"print(\"instance extends object? \", isinstance(i, object))\n",
"print(\"method extends object? \", isinstance(i.method, object))"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"So when I construct a class I'm effectively constructing an object, aren't I?\n",
"\n",
"What's the type of a class object?"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"type"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(MyClass)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Mmh, that's interesting. Let's check the manual."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"type?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"```\n",
"Init signature: type(self, /, *args, **kwargs)\n",
"Docstring: \n",
"type(object) -> the object's type\n",
"type(name, bases, dict, **kwds) -> a new type\n",
"Type: type\n",
"Subclasses: ABCMeta, EnumType, _AnyMeta, NamedTupleMeta, _TypedDictMeta, _DeprecatedType, PyCStructType, UnionType, PyCPointerType, PyCArrayType, ...\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"What if we..."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"__main__.TypeName"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"t = type(\"TypeName\", (), {})\n",
"t"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"<__main__.TypeName at 0x7fab944aae50>"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"t()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Very interesting. Let's try one more thing"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"def init(self, value):\n",
" print(\"Greetings from the constructor\")\n",
" self.instance_attribute = value\n",
"\n",
"def method(self):\n",
" print(f\"I'm a method! {self.instance_attribute}\")\n",
"\n",
"OtherClass = type(\"OtherClass\", (), {\n",
" \"__init__\": init,\n",
" \"method\": method\n",
"})"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"class: <class '__main__.OtherClass'>\n",
"Greetings from the constructor\n",
"instance: <__main__.OtherClass object at 0x7fab94532f50>\n",
"I'm a method! pumpkin\n"
]
}
],
"source": [
"print(\"class:\", OtherClass)\n",
"\n",
"i1 = OtherClass(\"pumpkin\")\n",
"print(\"instance:\", i1)\n",
"\n",
"i1.method()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"How did that work?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Object construction\n",
"\n",
"Let's refresh the steps for object construction."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## \\_\\_new\\_\\_() and \\_\\_init\\_\\_()\n",
"\n",
"You thought `__init__()` was the constructor in Python. It's not!\n",
"\n",
"`__new__()` constructs objects, `__init__()` initializes them."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"`__new__()` is a **class method** which takes the same arguments as `__init__()`, while `__init__()` is an **instance method**.\n",
"\n",
"Let's play with them."
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"scrolled": true,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Constructed object <__main__.Class object at 0x7fab945335d0> of <class '__main__.Class'> with value 42\n",
"Initializing object <__main__.Class object at 0x7fab945335d0> with value 42\n"
]
}
],
"source": [
"class Class:\n",
" def __new__(cls, value):\n",
" i = super().__new__(cls)\n",
" print(f\"Constructed object {i} of {cls} with value {value}\")\n",
" return i\n",
" \n",
" def __init__(self, value):\n",
" print(f\"Initializing object {self} with value {value}\")\n",
" self.value = value\n",
"\n",
"o = Class(42)"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"ename": "TypeError",
"evalue": "WrongClass.__new__() takes 1 positional argument but 2 were given",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[0;32mIn[24], line 7\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, value):\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvalue \u001b[38;5;241m=\u001b[39m value\n\u001b[0;32m----> 7\u001b[0m \u001b[43mWrongClass\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\n",
"\u001b[0;31mTypeError\u001b[0m: WrongClass.__new__() takes 1 positional argument but 2 were given"
]
}
],
"source": [
"class WrongClass:\n",
" def __new__(cls):\n",
" return super().__new__(cls)\n",
" def __init__(self, value):\n",
" self.value = value\n",
"\n",
"WrongClass(1)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Class construction\n",
"\n",
"We saw how to construct an object, and we said that a class is an object.\n",
"\n",
"But earlier we saw that in order to construct a class we had to call the `type()` function with 3 arguments.\n",
"\n",
"Is it a function? Let's see"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Type of function: <class 'function'>\n",
"Type of `type`: <class 'type'>\n"
]
}
],
"source": [
"def foo():\n",
" print(\"Hello world!\")\n",
"\n",
"print(\"Type of function: \", type(foo))\n",
"print(\"Type of `type`: \", type(type))"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"So the type of `type` is `type`. This Python is weird isn't it?\n",
"\n",
"So is it a function or what is it?\n",
"\n",
"### What is a function after all?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Doh, it's an object!!\n",
"\n",
"It's an object that implements `__call__()`!"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<function foo at 0x7fab94508040>\n",
"Hello world!\n",
"Hello world!\n",
"Hello world!\n"
]
}
],
"source": [
"def foo():\n",
" print(\"Hello world!\")\n",
"\n",
"print(foo)\n",
"\n",
"foo()\n",
"foo.__call__()\n",
"foo.__call__.__call__.__call__.__call__.__call__.__call__()"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<__main__.LookMomImAFunction object at 0x7fab7df61f50>\n",
"Bananas\n",
"Bananas\n",
"Bananas\n"
]
}
],
"source": [
"class LookMomImAFunction:\n",
" def __call__(self):\n",
" print(\"Bananas\")\n",
"\n",
"fun_class = LookMomImAFunction()\n",
"print(fun_class)\n",
"\n",
"fun_class()\n",
"fun_class.__call__()\n",
"fun_class.__call__.__call__.__call__.__call__.__call__()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Let's try something:"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<class '__main__.TypeName'>\n",
"<__main__.TypeName object at 0x7fab7df639d0>\n"
]
}
],
"source": [
"TypeName = type.__call__(type, \"TypeName\", (), {})\n",
"print(TypeName)\n",
"print(TypeName())"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"(I actually had to add an extra argument but don't worry about it for now)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### So what is `type`?\n",
"\n",
"Is it an object, a function, a class, a bird, a plane?\n",
"\n",
"We saw that..."
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 31,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(type) == type"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"So `type` is the type of itself.\n",
"\n",
"In other words `type` is an instance of the class `type`, or an instance of itself."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"You could almost say that `type` is a *metatype* or *metaclass*.\n",
"\n",
"This name sounds familiar, what a coincidence!"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Modifying the construction of a class\n",
"\n",
"Why would we want to do that?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Metaclasses\n",
"\n",
"As we saw, metaclasses are the type of the type, the class of the class.\n",
"\n",
"They are useful to tweak the creation of the class itself and, for instance, autogenerate methods."
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"class TypeChild(type):\n",
" def __new__(cls, name, bases, attributes):\n",
" print(f\"Constructing class {name}, child of {bases}, containing {attributes}\")\n",
" i = super().__new__(cls, name, bases, attributes)\n",
" print(i)\n",
" return i\n",
" \n",
" def __init__(self, name, bases, attributes):\n",
" print(f\"Initializing class {name}\")\n",
" super().__init__(name, bases, attributes)"
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {
"scrolled": true,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Constructing class ClassFoo, child of (), containing {'foo': 'bar'}\n",
"<class '__main__.ClassFoo'>\n",
"Initializing class ClassFoo\n",
"\n",
"new class: <class '__main__.ClassFoo'>\n",
"ClassFoo.foo: bar\n"
]
}
],
"source": [
"ClassFoo = TypeChild(\"ClassFoo\", (), {\"foo\": \"bar\"})\n",
"print()\n",
"print(\"new class: \", ClassFoo)\n",
"print(\"ClassFoo.foo: \", ClassFoo.foo)"
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {
"scrolled": true,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"type of class: <class '__main__.TypeChild'>\n"
]
}
],
"source": [
"print(\"type of class: \", type(ClassFoo))"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"So the type of this new class it's no longer `type`, it's `TypeChild`!\n",
"\n",
"What if we had a better syntax for this behavior..."
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {
"scrolled": true,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Constructing class ClassBar, child of (), containing {'__module__': '__main__', '__qualname__': 'ClassBar', 'foo': 'bar'}\n",
"<class '__main__.ClassBar'>\n",
"Initializing class ClassBar\n",
"\n",
"new class: <class '__main__.ClassFoo'>\n",
"ClassFoo.foo: bar\n",
"type of class: <class '__main__.TypeChild'>\n"
]
}
],
"source": [
"class ClassBar(metaclass=TypeChild):\n",
" foo = \"bar\"\n",
"\n",
"print()\n",
"print(\"new class: \", ClassFoo)\n",
"print(\"ClassFoo.foo: \", ClassFoo.foo)\n",
"print(\"type of class: \", type(ClassFoo))"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Invoking costructors\n",
"\n",
"Now that we saw all of this, another question. Is there anything special about the class construction syntax `ClassName()`?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"No."
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"class AnotherMetaclass(TypeChild):\n",
" def __call__(self, *args, **kwargs):\n",
" print(\"About to invoke class constructor with args\", args, kwargs)\n",
" return super().__call__(*args, **kwargs)"
]
},
{
"cell_type": "code",
"execution_count": 53,
"metadata": {
"scrolled": true,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Constructing class Foo, child of (), containing {'__module__': '__main__', '__qualname__': 'Foo', 'attr': 'asd', '__new__': <function Foo.__new__ at 0x7fab9455eac0>, '__init__': <function Foo.__init__ at 0x7fab9455e200>, '__classcell__': <cell at 0x7fab97e8f910: empty>}\n",
"<class '__main__.Foo'>\n",
"Initializing class Foo\n",
"About to invoke class constructor with args (69,) {}\n",
"__new__\n",
"__init__\n",
"<__main__.Foo object at 0x7fab7df624d0>\n",
"About to invoke class constructor with args (1,) {}\n",
"__new__\n",
"__init__\n"
]
}
],
"source": [
"class Foo(metaclass=AnotherMetaclass):\n",
" attr = \"asd\"\n",
" \n",
" def __new__(cls, val):\n",
" print(\"__new__\")\n",
" return super().__new__(cls)\n",
" \n",
" def __init__(cls, val):\n",
" print(\"__init__\")\n",
" \n",
"print(Foo(69))\n",
"f = Foo(1)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"As a matter of fact we could also define the metaclass for a metaclass and override `__call__()`, though that makes things very messy and we won't try."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## What about `self`?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Why is Python so `self`ish?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Let's start by learning that no, there's nothing special about the name \"self\" in and of it*self*."
]
},
{
"cell_type": "code",
"execution_count": 56,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<__main__.Lol object at 0x7fab7df96f90>\n",
"<__main__.Lol object at 0x7fab7df96f90>\n"
]
}
],
"source": [
"class Lol:\n",
" def meth(amphetamine):\n",
" print(amphetamine)\n",
" def method(self):\n",
" print(self)\n",
"\n",
"lol = Lol()\n",
"lol.meth()\n",
"lol.method()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"What is indeed special is the position, the first argument.\n",
"\n",
"Let's play a bit."
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"I am an instance method: <__main__.Lol object at 0x7fab9835ea50>\n",
"I'm a lonely sad function :(\n"
]
}
],
"source": [
"def external_method(self = None):\n",
" if self:\n",
" print(\"I am an instance method:\", self)\n",
" else:\n",
" print(\"I'm a lonely sad function :(\")\n",
"\n",
"# New instance\n",
"Lol.external_method = external_method\n",
"new_lol = Lol()\n",
"new_lol.external_method()\n",
"\n",
"# Old instance\n",
"lol.external_method = external_method\n",
"lol.external_method()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Why does only one of them get the automatic `self` argument?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Descriptor protocol"
]
},
{
"cell_type": "code",
"execution_count": 61,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"I am an instance method: <__main__.Lol object at 0x7fab7df96f90>\n"
]
}
],
"source": [
"lol.external_method = external_method.__get__(lol, type(lol))\n",
"lol.external_method()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"What's this cursed syntax?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"In short, when a class instance attribute is retrieved, it gives a chance to the retrieved data to modify the retrieval process."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"The magic is all within `__getattribute__()`, the hidden method that, given an object and a name, returns an attribute with that name."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"The default `__getattribute__()` of objects works somewhat like this:\n",
"* If `name` is in the the dictionary of **instance** attributes\n",
" * Return it\n",
"* For each base class in the *method resolution order*\n",
" * If `name` is in the the dictionary of **class** attributes\n",
" * If it implements the descriptor protocol:\n",
" * Ask it what it wants to be returned\n",
" * Else: Return it"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Let's write our own makeshift method"
]
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {},
"outputs": [],
"source": [
"class DrunkMethod:\n",
" def __init__(self, bound_instance = None):\n",
" self.bound_instance = bound_instance\n",
" \n",
" def __get__(self, instance, inst_type = None):\n",
" return type(self)(instance)\n",
" \n",
" def __call__(self):\n",
" if self.bound_instance:\n",
" print(f\"I'm a method of {self.bound_instance}\")\n",
" else:\n",
" print(\"I'm a standalone function\")"
]
},
{
"cell_type": "code",
"execution_count": 64,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"I'm a standalone function\n",
"I'm a method of <__main__.MyClass object at 0x7fab7df438d0>\n"
]
}
],
"source": [
"class MyClass:\n",
" method = DrunkMethod()\n",
" \n",
"MyClass.method()\n",
"\n",
"i = MyClass()\n",
"i.method()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Let's see another useful use of descriptors"
]
},
{
"cell_type": "code",
"execution_count": 81,
"metadata": {},
"outputs": [],
"source": [
"class LoggedAttr:\n",
" def __init__(self, default_value = None):\n",
" self.default = default_value\n",
" self.name = None\n",
" \n",
" def __set_name__(self, clazz, attr_name):\n",
" print(f\"LoggedAttr was assigned in {clazz} with name '{attr_name}'\")\n",
" self.name = attr_name\n",
"\n",
" def __get__(self, inst, inst_ty = None):\n",
" print(f\"Obtaining '{self.name}' of {inst}\")\n",
" return getattr(inst, f\"_{self.name}\", self.default)\n",
" \n",
" def __set__(self, inst, value):\n",
" print(f\"Setting '{self.name}' of {inst} to {value}\")\n",
" setattr(inst, f\"_{self.name}\", value)\n",
" \n",
" def __delete__(self, inst):\n",
" print(f\"Destroying '{self.name}' in {inst}\")\n",
" delattr(inst, f\"_{self.name}\")"
]
},
{
"cell_type": "code",
"execution_count": 83,
"metadata": {
"scrolled": true,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"LoggedAttr was assigned in <class '__main__.MyClass'> with name 'field'\n",
"Obtaining 'field' of <__main__.MyClass object at 0x7fab97e57b90>\n",
"Setting 'field' of <__main__.MyClass object at 0x7fab97e57b90> to bar\n",
"Destroying 'field' in <__main__.MyClass object at 0x7fab97e57b90>\n"
]
}
],
"source": [
"class MyClass:\n",
" field = LoggedAttr(\"foo\") # Descriptor instance assigned to the class\n",
"\n",
"i = MyClass()\n",
"v = i.field\n",
"i.field = \"bar\"\n",
"del i.field"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The end."
]
}
],
"metadata": {
"celltoolbar": "Slideshow",
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment