The package allows static typing during execution time.
It is built based on meta-programming and uses pure python functionality, no package is required to be installed.
It also supports different tools and functions such as multi dispatch, decorators, ... etc.
The package is currently in beta version. A lot of work including testing needs to be done to be released.
from py_structure.fields import Range, Float, typeassert
@typeassert(name=str, shares=Range(min_val=5, max_val=20), price=Float)
class Stock:
...
It is a lite way to construct a class similar to Structure, but it has some limitations showed in warnings.
Warnings & Notes:
- Inheritance is supported, but using same decorator for children is not applicable.
from py_structure.fields import Range, String, Email, URL, typeassert
@typeassert(name=String, salary=Range(min_val=4000, max_val=10000), email=Email(), url=URL())
class Employee:
...
# Working example
class UpdatedEmployee(Employee):
...
# None Working example, throws RecursionError: maximum recursion depth exceeded
@typeassert(permission=str)
class UpdatedEmployee2(Employee):
...
- It doesn't support default values of descriptors, use Structure class from base instead.
- In case the init function is constructed in the class, it needs to support multi args and kwargs.
def __init__(self, *args, **kwargs):
...
typeassert decorator to guarantee static typing, descriptors and built in types are allowed.
from py_structure.fields import Range, Email, URL, typeassert
@strucassert(name=str, salary=Range(min_val=4000, max_val=10000), email=Email(), url=URL())
class Employee:
...
Warnings & Notes:
- Inheritance is supported, but using same decorator for children is not applicable.
- It supports default values of descriptors.
from py_structure.base import Structure
from py_structure.fields import Range, String, Float, Email, URL
class Stock(Structure):
name = String()
shares = Range(min_val=5, max_val=20)
price = Float()
class Employee(Structure):
name = String(init=False, default='none')
salary = Range(min_val=4000, max_val=10000, init=False, default=5000)
email = Email()
url = URL()
Warnings & Notes:
- Inheritance is supported.
- It supports default values of descriptors.
- It supports different methods
# creating instance from previous example
employee = Employee("[email protected]", "http://google.com")
# names of all fields
print(employee.get_fields_name())
# descriptor of all fields
print(employee.get_all_fields())
# name & descriptor zipped together
print(employee.get_all_fields_name())
# frozen fields as descriptor
print(employee.get_frozen_fields())
Base Structure i useful for representing different data types
Metaclass that allows multiple dispatch of methods with different annotations.
When the method is called, the class invokes the method which has annotations that
follow the parameter types. In case of the parameters` types don't follow any methods` annotations, an error will be thrown.
from py_structure.multi_dispatch import MultiMethodMeta
class Spam(metaclass=MultiMethodMeta):
def bar(self, x: int, y: int):
print('Bar 1:', x, y)
def bar(self, s: str, n: int = 0):
print('Bar 2:', s, n)
s = Spam()
s.bar(2, 3)
s.bar('hello')
s.bar('hello', 5)
s.bar(2, 'hello') # will throw an error
Different protocols and patterns were implemented for a variety of use.
It is used for constructing fields that used in structure.
from py_tructure.fileds import(
Typed,
Int, Float, Decimal, Positive, Negative, Range, Binary, Hex, Oct,
PositiveInt, NegativeInt, PositiveFloat, NegativeFloat, PositiveDecimal, NegativeDecimal,
String, SizedString, RegexString, Email, URL, Slug,
DateTime,
Choice
)
It is used for constructing frozen fields that used in structure.
The frozen descriptor ensures that the value wouldn't be changed during execution time.
Corresponding to keyword static in other languages.
from py_structure.fields import typeassert
from py_structure.protocols import FrozenDescriptor
class FrozenString(FrozenDescriptor):
""" Frozen String Field """
typ: type = str
def __set__(self, instance, value):
if not isinstance(value, self.typ):
raise TypeError("Invalid Type, It should be String")
super(FrozenString, self).__set__(instance, value)
@typeassert(name=FrozenString)
class Employee:
...
A Singleton pattern allows creating just one instance of a class during the execution time.
from py_structure.protocols import Singleton
class UniqueClass(metaclass=Singleton):
...
a = UniqueClass()
b = UniqueClass()
print(a is b)
print(a == b)
Proxy protocol allows to provide the replacement for another object. Through using different classes to represent the functionalities of another one.
from py_structure.protocols import Proxy
class First:
def __init__(self, name: str):
self.name = name
a = First('a')
p = Proxy(a)
print(p.name)
Different tools and context managers were implemented for a variety of use.
Timer used to calculate time during execution, used as decorator and context manager
from py_structure.tools import Timer
TIME_LIMIT = 0.5
def countdown(n):
while n > 0:
n -= 1
yield n
with Timer() as t:
for _ in countdown(100_000_000):
if t.current_time > TIME_LIMIT:
print(f'Can`t exceed the time limit ({TIME_LIMIT})')
break
print(t.elapsed)
Count the number of invoking objects, used as decorator and context manager
from py_structure.tools import Counter
c = Counter(func=countdown)
with c:
for _ in range(100):
c(5)
print(c._count)
It returns a cached reference to a previous instance created with the same arguments (if any).
from py_structure.tools import Cache
class CachedCls(metaclass=Cache):
def __init__(self, name: str):
self.name = name
a = CachedCls('a')
b = CachedCls('b')
Turn attribute into lazy ones, useful for highly computational invoking, used as a decorator or metaclass
from py_structure.tools import lazyproperty
class Compute:
def __init__(self, x, y):
self.x = x
self.y = y
@lazyproperty
def mult(self):
print('Multiply x & y')
return self.x * self.y
@lazyproperty
def div(self):
print('Divide x by y')
return self.x / self.y
c = Compute(10, 5)
print(c.mult())
print(c.div())
The LazyCLass
could be used to turn all methods into lazy ones,instead of manually writing decorators for all methods Compute(metaclass=LazyCLass)
Flatt nested iterable into one sequence, used as normal function
from py_structure.utilities import flatten
nested_lst = [1, 4, 5, [4, [5, 8], 2], 6, 8]
flatten_lst = [i for i in flatten(nested_lst)]
print(nested_lst)
print(flatten_lst)
Sort an iterable based on another
from py_structure.utilities import sort_list_by_another
base = [1, 2, 4, 3, 5]
lst = [10, 12, 7, 11, 9]
print(sort_list_by_another(lst, base))