I? Python developer since v2.4 o Descriptors have not changed since Python 2.2, though we’re using Python 3 today Worked in industrial automation, metaverse development, and financial industry Previously spoken about descriptors in talks at PyCaribbean 2016, PyGotham 2017, EuroPython, PyCascades, and PyTennesssee Currently working at Bloomberg in the Data License group o We deliver financial data to clients o We use Flask, Celery, pycsvw, Redis, Jena, Hadoop, Perl, JavaScript, C/C++, Fortran o We are enterprise scale, but extremely agile
a glance Bloomberg Terminal Trading Solutions Tradebook Enterprise Data & Content News Media Bloomberg Law Bloomberg New Energy Finance Bloomberg Government
by the numbers • 5,000+ software engineers • 150+ technologists and data scientists devoted to machine learning • One of the largest private networks in the world • 120 billion pieces of data from the financial markets each day, with a peak of more than 10 million messages/second • 2 million news stories ingested / published each day (500+ news stories ingested/second) • News content ingested from 125K+ sources • Over 1 billion messages and Instant Bloomberg (IB) chats handled daily
sitting between an instance(o) and an attribute(x) it serves as syntactic sugar in place of getattr(o,'x'). In the simplest case this reduces further to o.__dict__['x'] {'x': 3} #1. self.__dict__[key] class MyObject: pass o = MyObject() o.__dict__['x'] = 3 o.__dict__ o.x 3
could be a class attribute 4 #2. self.__class__.__dict__[key] class BaseObject: pass class MyObject(BaseObject): pass o = MyObject() MyObject.y = 4 o.__class__.__dict__ o.y mappingproxy({'__doc__': None, '__module__': '__main__', 'y': 4})
way more we can do. This is old and boring stuff, but look at how powerful it is! called __getattr__ print p before set __getattr__(r) called class Probe: def __getattr__(self,attr): ret = "__getattr__(%s)"%attr print('called __getattr__') if attr=='r': return ret raise AttributeError #4. self.__class__.__getattr__(self,attr) p = Probe() print("print p before set",p.r,"called") p.r = 3 print("print p after set",p.r,"access is direct") print p after set 3 access is direct
On the left side, we have __setattr__ {} class Probe: def __setattr__(self,attr,value): print('called __setattr__') #self.__class__.__setattr__(self,attr,value) p = Probe() p.t = 3 p.__dict__ p.__dict__ called __setattr
Anyvar class Probe: def __getattribute__(self, attr): print('__getattribute__(%s)'%attr) return 'Anyvar' #5. self.__class__.__getattribute__(self,attr) p = Probe() print("print p before set",p.s) p.s = "hello" print("print p after set",p.s) __getattribute__(s) print p before set Anyvar
in the class.__dict__ __get__(<__main__.MyObject object at 0x7f74a84ff470>,<class '__main__.MyObject'>) class ProbeND: def __get__(self, obj, type=None): ret = '__get__(%s,%s)'%(obj,type) return ret #6. self.__class__.__dict__[attr].__get__(self,cls) o = MyObject() MyObject.z = ProbeND() # adding to __class__.__dict__ o.z o.__dict__['z'] = None o.z None
Protocol Initially included with Python 2.2 “New Style Classes” class Descriptor(object): def __get__(self, obj, objtype): # obj is None when called on the class return None def __set__(self, obj, val): pass def __delete__(self, obj): pass # Python 3.6 def __set_name__(self, owner, name): # owner is class, name is attr name self.name = name Data Descriptor Non-Data Descriptor
at all method class Greeter(): def greeting(self): print('Hi') Greeter.__dict__['greeting'].__class__ Greeter.__dict__['greeting'].__get__ function bound = Greeter().greeting bound.__class__ <method-wrapper '__get__' of function object at 0x7f74a850e598> Hi bound()
I use and when? Any metaprogramming technique is a shovel – a technique to get you out of a hole which is best held back until you are sure the benefit is worth the added complexity Technique Example Usage Normal Attribute Access Data Storage __getattr__ Caching, Lazy Execution __getattribute__ Proxy access to an unknown, remote, API Non Data Descriptors (__get__) Method Binding Data Descriptors (__get__ & __set__) Runtime typing