A Pack of useful python QoL changes
Project description
PyQoL
This package contains a lot of feature for Quality of Life, functionnal programming, and others.
.CORE
codedit
Codedit is a decorator that lets you change the source code of a function using regexes. Sadly the syntax has to be "python-valid" before, but the code doesn't have to mean anything though.
@codedit("oooooooone", "1")
def add_one(x):
return oooooooone
@codedit(r"_(.+)_more_than\[(.+)\]", r"\2 + \1")
def add_one(x):
return _1_more_than[x]
Obviously, in that case it's not that useful, but I'm sure you can find some hacks using it :)
Codedits
Is a class that contains all known useful codedits.
.Lambda
Lets you define a custom lambda operator.
@Codedits.Lambda(">>")
def add_one(x):
ld = {a >> a + 1}
return ld(x)
Valid lambdas include: -1>
, >
, :
, >>
, ..
.Bittors
(better iterators)
I
is the new range
I
, and its brothers IR
, IC
, IE
are iteration functions, used to create loops.
It will iterate over any iterable, and transform a int
argument into a range from 0
to this int
. Negative int
s lead to a backwards loop.
It will iterate over all arguments at the same time, and zip them.
Arguments are:
I(*args, revserse = False, enum = False, chunking=False, chunk_size=1)
Enum = True
is the same as zipping with I(None)
, which returns an infinite loop. It also can be called with IE
Chunking, also called by IC
returns multiple values at once, in a tuple.
IC(-8, chunk_size=2) -> (7, 6), (5, 4), (3, 2), (1, 0)
No
start, end, step
here, all arguments are the iterations
.Structs
Struct
Struct takes any arguments when created, and stores them. It is similar to a JS object.
my_object = Struct(health=100, strength=20)
my_object.sword = Swords.Diamond
def _run(self: Struct):
pass
my_object.run = _run
my_object.run()
Registry
You can create registers of functions (for plugin management, or special scoping), by creating a registry:
r = Registry()
Then, you can register functions in it, and access them that way.
@r.register
def my_happy_little_function(x):
print(f"happy little {x}")
r.registry["my_happy_little_function"]("accident")
r["my_happy_little_function"]("programmer")
.FP
Function
You can decorate one of your functions with Function
to access function composition, and other features.
@Function
def add_two(n): return n + 2
mult_by_two = Function(lambda x: x * 2)
add_then_mult = (add_two + mult_by_two)
mult_then_add = (add_two * mult_by_two)
Bunction
(better function)
This class is a superset of Function
, which allows for cool setups. Let's implement the fibonacci function with it, in a very defensive manner:
# First, setup the default case
@Bunction
def fib(n):
return fib(n-1) + fib(n-2)
Alone, this function doesn't work, it needs to return 1
if the input is 0
or 1
. We can easily patch this by adding cases, which will overwrite the default.
#if x is 0, this case will be executed
@fib.case(lambda x : x == 0)
def _one(x): return 1
Yes, this is ugly, that's why you can also do this:
# if input is 1, return 1
fib.case(1)(1)
Now, let's do a little defensive programming, and make our function idiot-proof:
fib.case(lambda x : x < 0)(0)
We can even preprocess the inputs, to fit in one of our cases when it couldn't before
@fib.preprocess(lambda x : type(x) == str)
def _exec(x):
if x.isnumber(): # implement your own isnumber, python doesn't have one for floats for some reason
return float(x) # will then be converted to a float
else:
return 0 # will input 0 to fib
# Then, convert floats to ints
fib.preprocess(lambda x : type(x) == float)(lambda x : int(x))
Map
A new tool for iterating, the Map
Map.over
Map over is a curried function taking in an iterable, then a function, and outputs a new L
ist of the results of the function. You can use it as a decorator, or as a normal function call
Map.over([0, 1, 2, 3])(lambda x : x * 2) == L(0, 2, 4, 6)
@Map.over([0, 1, 2, 3])
def newlist(e):
return e * 2
newlist == L(0, 2, 4, 6)
That last functionnality might look extremely wierd, and it does, but it can be practical if used correctly. If you have a set of objects you iterate over everywhere in the code, that you might change, why not have them all in once place ?
agents = L(...)
ForAllAgents = Map.over(agents)
#...
@ForAllAgents
def training_log(agent):
#...
return log_info
print(training_log)
Map.using
It is the exact same as Map.over
, but the argument order is swapped.
@Map.using
def mult_list_by_two(e):
return e * 2
mult_list_by_two([0, 1, 2, 3]) == L(0, 2, 4, 6)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.