ez-life

A Python Package to make life ez for pythonistas


Keywords
ez-life, notebooks, python-scripts
License
MIT
Install
pip install ez-life==1.1.0

Documentation

ez_life

The objective of ez_life is to make coding with Python easier by removing repetitive code while still maintaining the same level of functionality

Installation

pip install -U ez_life

param2attr

  • Here is a little sneak peak of what you can do with this package!
    • First consider this code block:
from ez_life import Param2attr

class Foo: 
	def __init__(self, param1 = None, param2 = None, param3 = None): 
		# This sux 
		self.param1 = param1 
		self.param2 = param2 
		self.param3 = param3
  • We can instead create a class that looks like this, using a property decorator to perform the param to attribute assignments
class Foo: 
	@Param2attr(exclude=None) 
	def __init__(self, param1 = None, param2 = None, param3 = None): 
		# this good, allows u to write other code here during initialization 
		pass
  • If you are interested to learn about the implementation or other features that param2attr supports, feel free to read the Param2attr documentation!

JTProperty

  • Ez_life can also be used to create "variable dependencies" using the @JTProperty decorator
    • It 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 refresher

Classic Setter and Getter Methods

  • First consider a classic class Square
    • A square has a 3 properties:
      • A length, an perimeter and an area
    • First we will code the length:
class Square: 
	def getLength(self): 
		return self._length # the underscore "_" in "_length" means that it is a "protected variable"
	
	def setLength(self, r): 
		if r <= 0: 
			raise ValueError("length should be greater than 0") 
		self._length = r
  • We can use this class as follows:
square = Square() # create a class instance
square.setLength(4) # sets self._length to 4
print(square.getLength()) # 4
square.setLength(-1) # raises a ValueError "length should be greater than 0"

Property Setter and Getter Methods Part 1

  • Python's inbuilt @property decorator allows setters and getters to be used in a much easier way:
class Square:
	@property 
	def length(self): # the setter method
		return self._length 
	
	@length.setter 
	def length(self, r): # the getter method 
		if r <= 0: 
			raise ValueError("length should be greater than 0") 
		self._length = r
  • The code below shows how to use the setter and getter methods for this class:
square = Square() # create a square instance
square.length = 4 # sets self._length to 4
print(square.length) # 4
square.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 getter
    • The code below demonstrates this vulnerability:
square = Square() 
print(square.length) # raises an error

Property Setter and Getter Methods Part 2

  • The reason why an error is raised is because the length has not been set yet via the setter method
    • A solution to this could be achieved as follows:
class Square: 
	@property 
	def length(self): 
		if "_length" not in dir(self): # checks if self._length is an attribute yet 
			self.length = 1 # if not, call the setter method to set self._length to 1 
		return self._length 

	@length.setter 
	def length(self, r): 
		if r <= 0: 
			raise ValueError("length should be greater than 0") 
		self._length = r
  • Now we will not get an error when calling the getter before the setter, we instead get some default value
    • In this case the default value is 1
square = Square() 
print(square.length) # 1

Understanding The @JTProperty:

JTProperty Setter and Getter Methods

  • Obviously, the code with the @property decorator is Pythonic, however it is far from perfect
    • We can see that there are many lines of code already written for just the length setter and getter methods
  • Now 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 module
from ez_life import JTProperty 

class Square: 
	@JTProperty() 
	def length(self): 
		return 1 
	
	@length.setter 
	def length(self, r): 
		if r <= 0: 
			raise ValueError("length should be greater than 0") 
		return r
  • For JTProperty we can call the setter before the getter method
square = Square() # create a class instance
square.length = 4 # sets self._length to 4
print(square.length) # 4
square.length = -1 # raises a ValueError "length should be greater than 0"
  • Or we can call the getter before the setter method
square = Square() 
print(square.length) # 1
  • As we can see, JTProperty offers the same functionality as @property but with less code!

Full Implementation of Square with JTProperty

  • Now that we have an understanding of JTProperty, we can fully implement the square class perimeter and area setter methods!
class Square: 
	@JTProperty() 
	def length(self): 
		return 1 

	@length.setter 
	def length(self, r): 
		if r <= 0: 
			raise ValueError("length should be greater than 0") 
		return r 

	@JTProperty() 
	def area(self): 
		return self.length ** 2 
	
	@JTProperty() 
	def perimeter(self): 
		return self.length * 4
  • We can call these methods as follows:
square = Square()
print(square.area) # (1 ** 2) => 1
print(square.perimeter) # (1 * 4) => 4

square.length = 2 # set length to 2

print(square.area) # 1 <= note that square.area stays the same even though square.length changed
print(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 2
    • To 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:
class Square: 
	@JTProperty() 
	def length(self): 
		return 1 
	
	@length.setter 
	def length(self, r): 
		if r <= 0: 
			raise ValueError("length should be greater than 0") 
		return r 

	@JTProperty(deps = 'length') 
	def area(self): 
		return self.length ** 2 

	@JTProperty(deps = 'length') 
	def perimeter(self): 
		return self.length * 4
  • Now when changing square.length to some other value, both square.area and square.perimeter return their corresponding updated values when called
square = Square()
print(square.area) # (1 ** 2) => 1
print(square.perimeter) # (1 * 4) => 4

square.length = 2 # set length to 2

print(square.area) # (2 ** 2) => 4
print(square.perimeter) # (2 * 4) => 8
  • This concludes the introduction to some of the features that JTProperty supports!
  • Other features not covered here include:
    • Support for hierarchal classes
    • default and default type setters
    • Circular dependency graphs
  • If you are interested to learn about these other features and more, feel free to read the JTProperty documentation!

ez-life directory layout:

  • develop: the package containing all .ipynb dev files and converted .py files
    • param2attr.ipynb: dev notebook for automation of parameter creation to class object attributes
    • param2attr.py: param2attr.ipynb -> param2attr.py via Makefile
    • jt_property.ipynb: dev notebook for more writing dependency related code
    • jt_property.py: jt_property.ipynb -> jt_property.py via Makefile
  • ez_life: the package containing all converted .py files
  • .gitignore: for ignoring files when pushing and pulling
  • LICENSE: what u can and can't do with this repo
  • MAKEFILE: automated test cases and .ipynb to .py conversion
  • README: this file essentially
  • MANIFEST.in: Files to include in pip upload
  • build: automatically generated file for pip
  • dist: automatically generated file for pip
  • setup.py: contains setups for when uploading to pip
  • test*.py: test files for modules within ez_life