Package: ez-life
ez_lifeThe objective of ez_life is to make coding with Python easier by removing repetitive code while still maintaining the same level of functionalityInstallationpip install -U ez_lifeparam2attrHere is a little sneak peak of what you can do with this package!First consider this code block:fromez_lifeimportParam2attrclassFoo:def__init__(self,param1=None,param2=None,param3=None):# This suxself.param1=param1self.param2=param2self.param3=param3We can instead create a class that looks like this, using a property decorator to perform the param to attribute assignmentsclassFoo:@Param2attr(exclude=None)def__init__(self,param1=None,param2=None,param3=None):# this good, allows u to write other code here during initializationpassIf you are interested to learn about the implementation or other features that param2attr supports, feel free to read theParam2attr documentation!JTPropertyEz_life can also be used to create "variable dependencies" using the @JTProperty decoratorIt essentially builds upon the functionality that the @property decorator gives!Understanding The Classic @property:The concept of python's classic decorator @property is not difficult to learn, read this section if you want to learn it or get a refresherClassic Setter and Getter MethodsFirst consider a classic class SquareA square has a 3 properties:A length, an perimeter and an areaFirst we will code the length:classSquare:defgetLength(self):returnself._length# the underscore "_" in "_length" means that it is a "protected variable"defsetLength(self,r):ifr<=0:raiseValueError("length should be greater than 0")self._length=rWe can use this class as follows:square=Square()# create a class instancesquare.setLength(4)# sets self._length to 4print(square.getLength())# 4square.setLength(-1)# raises a ValueError "length should be greater than 0"Property Setter and Getter Methods Part 1Python's inbuilt @property decorator allows setters and getters to be used in a much easier way:classSquare:@propertydeflength(self):# the setter methodreturnself._length@length.setterdeflength(self,r):# the getter methodifr<=0:raiseValueError("length should be greater than 0")self._length=rThe code below shows how to use the setter and getter methods for this class:square=Square()# create a square instancesquare.length=4# sets self._length to 4print(square.length)# 4square.length=-1# raises a ValueError "length should be greater than 0"Syntactically, the code above is much more pythonic, but the problem is that the setter must be called before the getterThe code below demonstrates this vulnerability:square=Square()print(square.length)# raises an errorProperty Setter and Getter Methods Part 2The reason why an error is raised is because the length has not been set yet via the setter methodA solution to this could be achieved as follows:classSquare:@propertydeflength(self):if"_length"notindir(self):# checks if self._length is an attribute yetself.length=1# if not, call the setter method to set self._length to 1returnself._length@length.setterdeflength(self,r):ifr<=0:raiseValueError("length should be greater than 0")self._length=rNow we will not get an error when calling the getter before the setter, we instead get some default valueIn this case the default value is 1square=Square()print(square.length)# 1Understanding The @JTProperty:JTProperty Setter and Getter MethodsObviously, the code with the @property decorator is Pythonic, however it is far from perfectWe can see that there are many lines of code already written for just the length setter and getter methodsNow we will demonstrate how the ez_life @JTProperty decorator can make the Square class above more concise@JTProperty does this by abstracting away the self._length variable# importing the JTProperty decorator from the ez_life modulefromez_lifeimportJTPropertyclassSquare:@JTProperty()deflength(self):return1@length.setterdeflength(self,r):ifr<=0:raiseValueError("length should be greater than 0")returnrFor JTProperty we can call the setter before the getter methodsquare=Square()# create a class instancesquare.length=4# sets self._length to 4print(square.length)# 4square.length=-1# raises a ValueError "length should be greater than 0"Or we can call the getter before the setter methodsquare=Square()print(square.length)# 1As we can see, JTProperty offers the same functionality as @property but with less code!Full Implementation of Square with JTPropertyNow that we have an understanding of JTProperty, we can fully implement the square class perimeter and area setter methods!classSquare:@JTProperty()deflength(self):return1@length.setterdeflength(self,r):ifr<=0:raiseValueError("length should be greater than 0")returnr@JTProperty()defarea(self):returnself.length**2@JTProperty()defperimeter(self):returnself.length*4We can call these methods as follows:square=Square()print(square.area)# (1 ** 2) => 1print(square.perimeter)# (1 * 4) => 4square.length=2# set length to 2print(square.area)# 1 <= note that square.area stays the same even though square.length changedprint(square.perimeter)# 4 <= note that square.perimeter stays the same# square.area = 10 # (note that this gives an error because no setter method is defined for area)Full Implementation of Square with JTProperty part 2 (with dependencies)Notice that square.area and square.perimeter do not update when square.length is changed to 2To update square.area and square.perimeter when square.length is changed, a "dependency" list must be given to JTProperty for both square.area and square.perimeter methods:classSquare:@JTProperty()deflength(self):return1@length.setterdeflength(self,r):ifr<=0:raiseValueError("length should be greater than 0")returnr@JTProperty(deps='length')defarea(self):returnself.length**2@JTProperty(deps='length')defperimeter(self):returnself.length*4Now when changing square.length to some other value, both square.area and square.perimeter return their corresponding updated values when calledsquare=Square()print(square.area)# (1 ** 2) => 1print(square.perimeter)# (1 * 4) => 4square.length=2# set length to 2print(square.area)# (2 ** 2) => 4print(square.perimeter)# (2 * 4) => 8This concludes the introduction to some of the features that JTProperty supports!Other features not covered here include:Support for hierarchal classesdefault and default type settersCircular dependency graphsIf you are interested to learn about these other features and more, feel free to read theJTProperty documentation!ez-life directory layout:develop: the package containing all .ipynb dev files and converted .py filesparam2attr.ipynb: dev notebook for automation of parameter creation to class object attributesparam2attr.py: param2attr.ipynb -> param2attr.py via Makefilejt_property.ipynb: dev notebook for more writing dependency related codejt_property.py: jt_property.ipynb -> jt_property.py via Makefileez_life: the package containing all converted .py files.gitignore: for ignoring files when pushing and pullingLICENSE: what u can and can't do with this repoMAKEFILE: automated test cases and .ipynb to .py conversionREADME: this file essentiallyMANIFEST.in: Files to include in pip uploadbuild: automatically generated file for pipdist: automatically generated file for pipsetup.py: contains setups for when uploading to piptest*.py: test files for modules within ez_life