Protocols Flashcards
__bool__()
class Rectangle:
def __init__(self, w, h):
self. width = w
self. height = h
def __bool__(self):
if self.width and self.height:
return True
else:
return False
>>> bool(Rectangle(10, 15))
True
>>> bool(Rectangle(0, 1))
False
Also, if an object supplies a __len__() method, python will consider False objects of zero length.
and, or and not will now use this method
mathematical operators
- __add__(self, value) can be accessed with +
- __sub__(self, value) obvious
- __mul__(self, factor) obvious
- __truediv__(self, divisor) is ‘/’ - ie. 5/4 = 1.25
- __floordiv__(self, divisor) is ‘//’ - ie. 5/4 = 1
- __mod__(self, divisor) is ‘%’ - ie. 5/4 = 0.25
- __divmod__(self, divisor) is divmod() and returns a tuple, the floordiv and the modulo
- __pow__(self, power, modulo = None) uses ‘**’, <modulo></modulo>
Bitwise operations
Not terribly important to me
- __lshift__() __rshift__() shift bits on to left or right
- & AND, __and__()
- | or __or__() [one or both]
- ^ __xor__() [strictly one]
- ~ __invert__() [?] takes a number and returns the opposite, sorta.
operation variations
There is an operator, each has a left hand method, right hand method, and inline method.
right hand is the left hand prepended with ‘r’
__add__() becomes __radd__()
inline adds an “i”
__add__() becomes __iadd__()
division and modulo don’t have an inline method.
Bitwise inversion doesn’t have a right hand or inline method.
Numbers
- __index__(self) determines how a class will index itself
- round(3.14, 1) = 3.1
- round(3.14) = 3
- round(3.14, 0) = 3.0
import decimal
round(decimal.Decimal(‘3.14’), 1) == Decimal(‘3.1’)
round(decimal.Decimal(‘3.14’)) == 3
round(256, -1) == 260
round(512, -2) == 500
Sign Operations
- __neg__()
- __pos__() doesn’t really do anything
- __abs__()
Comparison operators
- __eq__()
- __ne__() - if you use __eq__, define this as well
- __lt__() and __lte__()
- __gt__() and __gte__()
Iterables
an object is iterable if passing it to the built-in iter() returns an iterator. iter() first inspects the object, looking for an __iter__() method
two things required
- __iter__() - not all that interesting. typically it just returns self and usually doesn’t need defining
- __next__() - where all the real work happens.
if Python sees something that stops a loop while the iterator still has values in it, the iterator remains with those values in it. This happens in break, return, or raise
raise StopIteration is what finally stops it
Repeatable Generators
now
def repeatable(generator): """a decorator to turn a generator into an object that can be iterated multiple times""" class RepeatableGenerator: def \_\_init\_\_(self, \*args, \*\*kwargs): self.args = args self.kwargs = kwargs def \_\_iter\_\_(self): return iter(generator(\*self.args, \*\*self.kwargs)) return RepeatableGenerator
@repeatable
def gen(max):
__for x in range(max):
____yield x
etc.
Problems with this example:
- give it a function, returns a class
- can’t be used as a method b/c has no __get__() method
- wrap function in functools.wraps
@functools.wraps(generator)
def wrapper(*args, **kwargs):
__return RepeatableGenerator(*args, __**kwargs)
**return wrapper **
instead of RepeatableGenerator
__getitem__()
__setitem__()
__getitem__():
- receives an index or slice object
- raises IndexError if index is out of range
__setitem__():
- also accepts index or slice, but also values or a value
- it is expected to remove item(s) from the index or slice and replace them with the new items, expanding or contracting the list of new items as necessary.
append and insert add items.
__setitem__ does not.
__contains__()
__delitem__()
remove()
del keyword is just like remove. Items after the deleted one are moved left. It calls the __delitem__() method rather than the explicit remove() method call.
in is the keyword that calls __contains__(). Contains accepts an object and returns True or False
Mappings
keys to mappings may be any hashable object:
- dates
- times
- strings
- integers
- tuples
slice objects are not hashable, so they can’t be keys
mapping methods
- keys(). __iter__ goes over the keys, should be defined if making custom mapping
- values() goes over the values
- items() iterates over k,v pairs in tuples
__call__()
makes an object callable. Anything can be callable
as a function, it can be decorated any number of times, but any decorator needs to account for the instance being passed as the first argument.
context managers
context managers are used in a with statement
Allows an object to define what it means to work within the context of that object - ex. setup and clean up.
two distinct steps:
**__enter__() : **
receives no additional arguments, just the instance. provides initialization of code block in with statement
if with statement includes an as clause, __enter__() returns a value to that variable
__exit__():
it’s job is to return the context to whatever it was prior to the with statement. In the case of files, it closes it. this is still called even if return, yield, continue, or break statements end the block
exit gets three arguments to determine if the block stopped normally or there was an exception. class of the exception, the instance of that class, and traceback object. Any implementation of exit must accept these three arguments.