Python avancé: Some examples.

This commit is contained in:
Julien Palard 2023-05-26 11:19:30 +02:00
parent 596e9c5ae1
commit e37af50649
Signed by: mdk
GPG Key ID: 0EFC1AC1006886F8
11 changed files with 366 additions and 26 deletions

View File

@ -0,0 +1,28 @@
# This is the creation « from scratch » of the following class:
# class Foo:
# def say(self):
# print("hello")
# First the type.__prepare__ is called to create a namespace, it
# typically returns a fresh empty dict:
ns = {}
# Then the class body is executed in this namespace:
exec(
"""
def say(self):
print("hello")
""",
globals(),
ns,
)
# Now, the type __new__ is called to create the class:
Foo = type.__new__(type, "Foo", (), ns)
type.__init__(Foo, "Foo", (), ns) # Does nothing
Foo().say()

View File

@ -0,0 +1,39 @@
from urllib.parse import parse_qsl
class Application:
def __init__(self):
self.routes = {}
def route(self, path):
def deco(fct):
self.routes[path] = fct
return fct
return deco
def get(self, path):
try:
path, qs = path.split("?", maxsplit=1)
except ValueError:
qs = {}
return self.routes[path](**dict(parse_qsl(qs)))
app = Application()
@app.route("/")
def home():
return "<a href='/api'>/api</a>"
@app.route("/api")
def api():
return {"version": "1"}
@app.route("/auth/")
def auth(username, password):
return f"Wrong password for {username}"
print(app.get("/"))
print(app.get("/api"))
print(app.get("/auth/?username=mdk&password=test"))

View File

@ -0,0 +1,19 @@
def memoize(limit): # Factory
def _memoize(function_fib):
memory = {}
def cached_fib(n):
if n in memory:
return memory[n]
result = function_fib(n)
memory[n] = result
return result
return cached_fib
return _memoize
@memoize(limit=1024)
def fib(n):
if n < 2:
return 1
return fib(n-1) + fib(n-2)
# fib = memoize(limit=1024)(fib)

View File

@ -0,0 +1,33 @@
class PositiveInteger:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, cls=None):
if instance is None:
return self
return instance.__dict__[self.name]
def __set__(self, instance, value):
if value < 0:
raise ValueError("Negative values are not acceptable.")
instance.__dict__[self.name] = value
class Order:
qty = PositiveInteger()
price = PositiveInteger()
def __init__(self, item, price, qty):
self.item = item
self.price = price
self.qty = qty
def cost(self):
return self.price * self.qty
# Order("un truc", -1, 1)
# o = Order("Un livre", 35, 1)
# o.qty += 1
# o.qty -= 10
# print(o.cost())

View File

@ -0,0 +1,14 @@
from string import ascii_letters
from hypothesis import given, settings
from hypothesis.strategies import text, lists
@given(lists(text(alphabet=ascii_letters, max_size=10)))
@settings(max_examples=500)
def test_sort_is_stable(a_list):
print(a_list)
double_sort = sorted(sorted(a_list, key=str.lower), key=len)
single_sort = sorted(a_list, key=lambda s: (len(s), s.lower()))
assert double_sort == single_sort

View File

@ -0,0 +1,27 @@
class AutoInit(type):
def __new__(mcs, name, bases, ns, **kwargs):
cls = type.__new__(mcs, name, bases, ns)
cls._attributes = kwargs["attributes"]
return cls
def __call__(self, *args, **kwargs):
attributes = {}
for name in self._attributes:
attributes[name] = kwargs.pop(name, None)
obj = super().__call__(*args, **kwargs)
for key, value in attributes.items():
setattr(obj, key, value)
return obj
class Point(metaclass=AutoInit, attributes=("x", "y", "z")):
...
origin = Point(x=0, y=0, z=0)
print(origin.x, origin.y, origin.z) # 0 0 0
NonePoint = Point()
print(NonePoint.x, NonePoint.y) # None None
# origin = Point(x=0, y=0, blah=42) # TypeError !

View File

@ -0,0 +1,66 @@
from functools import wraps
from time import perf_counter, sleep
from typing import Self
def as_call(args, kwargs):
passed = ['self']
for arg in args[1:]:
passed.append(repr(arg))
for key, value in kwargs.items():
passed.append(f"key={value!r}")
return "(" + ", ".join(passed) + ")"
def introspected_method(clsname, ctx, name, method):
def _(*args, **kwargs):
callstr = f"{clsname}.{name}{as_call(args, kwargs)}"
print(f"{'| ' * ctx['depth']}Calling {callstr}...")
ctx["depth"] += 1
before = perf_counter()
result = method(*args, **kwargs)
after = perf_counter()
ctx["depth"] -= 1
callstr = f"{clsname}.{name}{as_call(args, kwargs)}"
print(
f"{'| ' * ctx['depth']}Returning from {callstr} after {after-before:.2f}s with {result}"
)
return result
return _
class Introspected(type):
def __new__(cls, name, bases, ns, **kwds):
ctx = {"depth": 0}
for key, value in ns.items():
if callable(value) and key != "__repr__":
ns[key] = introspected_method(name, ctx, key, value)
return super().__new__(cls, name, bases, ns, **kwds)
class Point(metaclass=Introspected):
def __init__(self, x, y):
self.x = x
self.y = y
def dist(self, other: Self) -> float:
return ((other.x - self.x) ** 2 + (other.y - self.y) ** 2) ** 0.5
def __repr__(self):
return f"Point(x={self.x!r}, y={self.y!r})"
def modulus(self):
return self.dist(Point(0, 0))
def recursive_call(self, value):
sleep(0.1)
if value:
return self.recursive_call(value - 1)
else:
return self.mean()
if __name__ == "__main__":
p = Point(10, 10)
print(p.modulus())

View File

@ -0,0 +1,26 @@
class StronglyTypedMeta(type):
def __new__(cls, name, bases, ns, **kwds):
if '__annotations__' in ns:
for name, type in ns['__annotations__'].items():
ns[name] = StronglyTypedDescriptor(name, type)
return super().__new__(cls, name, bases, ns, **kwds)
class StronglyTypedDescriptor:
def __init__(self, name, type):
self.name = name
self.type = type
def __get__(self, instance, cls=None):
return instance.__dict__[self.name]
def __set__(self, instance, value):
if not isinstance(value, self.type):
raise TypeError(f"{self.name} should be of type {self.type}.")
instance.__dict__[self.name] = value
class Test(metaclass=StronglyTypedMeta):
x: str
y: int

View File

@ -0,0 +1,70 @@
class Meta(type):
@classmethod
def __prepare__(metacls, name, bases, **kwds):
"""Prepare the namespace in which the class will be executed."""
print(
"Meta.__prepare__(",
f"{metacls=},",
f"{name=},",
f"{bases=},",
f"{kwds=}) -> {{}}",
sep="\n ",
)
return {}
# __new__() is a static method (special-cased so you need not declare it as such)
def __new__(cls, name, bases, namespace, **kwds):
"""The class body is executed in a new namespace and the class name is
bound locally to the result of type(name, bases, namespace).
"""
print(
"\nMeta.__new__(",
f"{cls=},",
f"{name=},",
f"{bases=},",
f"{namespace=}",
f"{kwds=})",
sep="\n ",
end=" ",
)
returns = type.__new__(cls, name, bases, namespace)
print(f"-> {returns}\n")
return returns
def __init__(self, name, bases, namespace, **kwds):
"""This is the actual creation of the class !
As a class is an instance of its type."""
print(
"Meta.__init__(",
f"{self=},",
f"{name=},",
f"{bases=},",
f"{namespace=},",
f"{kwds=})",
sep="\n ",
end="\n\n"
)
class Classe(metaclass=Meta):
print("\nAfter Meta.__prepare__ is called, the interpreter executes the class body "
"like:")
print(" exec(body, globals(), ns)")
print("with `ns` the dict returned from __prepare__")
def __new__(cls, *args, **kwargs):
print(f"Classe.__new__({cls=}, {args=}, {kwargs=})")
returns = object.__new__(cls)
print(f" returns {returns}")
return returns
def __init__(self, *args, **kwargs):
print(f"Classe.__init__({self=}, {args=}, {kwargs=})")
print("Will now instantiate a Classe()")
Classe()

View File

@ -0,0 +1,44 @@
from pathlib import Path
class Context:
def __init__(self):
print("Constructing context")
def __enter__(self):
print("Entering context")
return "Coucou"
def __exit__(self, exc_type, exc_value, traceback):
print("Exiting context", exc_type)
# with Context() as ctx:
# print("In context", ctx)
from tempfile import NamedTemporaryFile
import os
class InplaceEdit:
def __init__(self, filename, encoding="UTF-8"):
self.filename = filename
self.ifile = open(filename, encoding=encoding)
self.encoding = encoding
self.tmpfile = NamedTemporaryFile(mode="w", delete=False, encoding=encoding, dir=Path(filename).parent)
def __enter__(self):
return self.ifile, self.tmpfile
def __exit__(self, typ, value, tb):
self.ifile.close()
self.tmpfile.close()
if typ is None:
os.rename(self.tmpfile.name, self.filename)
else:
os.unlink(self.tmpfile.name)
# with open("/etc/hosts") as hosts, open("/etc/shadow") as shadow:
# ...
with InplaceEdit("hosts.txt") as (ifile, ofile):
for line in ifile:
ofile.write(line.replace("www.mdk.fr", "mdk.fr"))

View File

@ -1,26 +0,0 @@
digraph Python {
subgraph cluster_0 {
node [style=filled];
label = "Names";
color = lightgrey;
un;
deux;
trois;
liste;
}
subgraph cluster_1 {
node [style=filled];
label = "Values";
color = lightgrey;
1;
2;
3;
4;
"[...]" -> 1;
"[...]" -> 2;
}
un -> 1;
deux -> 2;
trois -> 3;
liste -> "[...]";
}