dynamic attributes, descriptors and first concurrency examples
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"serial": 33451,
|
||||
"name": "Migrating to the Web Using Dart and Polymer - A Guide for Legacy OOP Developers",
|
||||
"event_type": "40-minute conference session",
|
||||
|
||||
|
||||
"time_start": "2014-07-23 17:00:00",
|
||||
"time_stop": "2014-07-23 17:40:00",
|
||||
"venue_serial": 1458,
|
||||
@@ -13304,4 +13304,4 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
23
metaprog/oscon-schedule/demo_schedule2.py
Normal file
23
metaprog/oscon-schedule/demo_schedule2.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import shelve
|
||||
|
||||
from schedule2 import DB_NAME, CONFERENCE, load_db
|
||||
from schedule2 import DbRecord, Event
|
||||
|
||||
with shelve.open(DB_NAME) as db:
|
||||
if CONFERENCE not in db:
|
||||
load_db(db)
|
||||
|
||||
DbRecord.set_db(db)
|
||||
event = DbRecord.fetch('event.33950')
|
||||
print(event)
|
||||
print(event.venue)
|
||||
print(event.venue.name)
|
||||
for spkr in event.speakers:
|
||||
print('{0.serial}: {0.name}'.format(spkr))
|
||||
|
||||
print(repr(Event.venue))
|
||||
|
||||
event2 = DbRecord.fetch('event.33451')
|
||||
print(event2)
|
||||
print(event2.fetch)
|
||||
print(event2.venue)
|
||||
@@ -9,14 +9,21 @@ explore1.py: Script to explore the OSCON schedule feed
|
||||
357
|
||||
>>> sorted(feed.Schedule.keys()) # <3>
|
||||
['conferences', 'events', 'speakers', 'venues']
|
||||
>>> feed.Schedule.speakers[-1].name # <4>
|
||||
>>> for key, value in sorted(feed.Schedule.items()): # <4>
|
||||
... print('{:3} {}'.format(len(value), key))
|
||||
...
|
||||
1 conferences
|
||||
484 events
|
||||
357 speakers
|
||||
53 venues
|
||||
>>> feed.Schedule.speakers[-1].name # <5>
|
||||
'Carina C. Zona'
|
||||
>>> talk = feed.Schedule.events[40] # <5>
|
||||
>>> talk = feed.Schedule.events[40] # <6>
|
||||
>>> talk.name
|
||||
'There *Will* Be Bugs'
|
||||
>>> talk.speakers # <6>
|
||||
>>> talk.speakers # <7>
|
||||
[3471, 5199]
|
||||
>>> talk.flavor # <7>
|
||||
>>> talk.flavor # <8>
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
KeyError: 'flavor'
|
||||
@@ -34,13 +41,13 @@ class FrozenJSON:
|
||||
"""
|
||||
|
||||
def __init__(self, mapping):
|
||||
self._data = dict(mapping) # <1>
|
||||
self.__data = dict(mapping) # <1>
|
||||
|
||||
def __getattr__(self, name): # <2>
|
||||
if hasattr(self._data, name):
|
||||
return getattr(self._data, name) # <3>
|
||||
if hasattr(self.__data, name):
|
||||
return getattr(self.__data, name) # <3>
|
||||
else:
|
||||
return FrozenJSON.build(self._data[name]) # <4>
|
||||
return FrozenJSON.build(self.__data[name]) # <4>
|
||||
|
||||
@classmethod
|
||||
def build(cls, obj): # <5>
|
||||
|
||||
@@ -40,11 +40,11 @@ class FrozenJSON:
|
||||
return arg
|
||||
|
||||
def __init__(self, mapping):
|
||||
self._data = dict(mapping)
|
||||
self.__data = dict(mapping)
|
||||
|
||||
def __getattr__(self, name):
|
||||
if hasattr(self._data, name):
|
||||
return getattr(self._data, name)
|
||||
if hasattr(self.__data, name):
|
||||
return getattr(self.__data, name)
|
||||
else:
|
||||
return FrozenJSON(self._data[name]) # <4>
|
||||
return FrozenJSON(self.__data[name]) # <4>
|
||||
# END EXPLORE2
|
||||
|
||||
@@ -10,10 +10,8 @@ schedule1.py: traversing OSCON schedule data
|
||||
>>> speaker = db['speaker.3471'] # <4>
|
||||
>>> type(speaker) # <5>
|
||||
<class 'schedule1.Record'>
|
||||
>>> speaker.name # <6>
|
||||
'Anna Martelli Ravenscroft'
|
||||
>>> speaker.twitter
|
||||
'annaraven'
|
||||
>>> speaker.name, speaker.twitter # <6>
|
||||
('Anna Martelli Ravenscroft', 'annaraven')
|
||||
>>> db.close() # <7>
|
||||
|
||||
# END SCHEDULE1_DEMO
|
||||
@@ -23,7 +21,7 @@ schedule1.py: traversing OSCON schedule data
|
||||
# BEGIN SCHEDULE1
|
||||
import warnings
|
||||
|
||||
import osconfeed
|
||||
import osconfeed # <1>
|
||||
|
||||
DB_NAME = 'data/schedule1_db'
|
||||
CONFERENCE = 'conference.115'
|
||||
@@ -31,17 +29,17 @@ CONFERENCE = 'conference.115'
|
||||
|
||||
class Record:
|
||||
def __init__(self, **kwargs):
|
||||
self.__dict__.update(kwargs) # <1>
|
||||
self.__dict__.update(kwargs) # <2>
|
||||
|
||||
|
||||
def load_db(db):
|
||||
raw_data = osconfeed.load() # <2>
|
||||
raw_data = osconfeed.load() # <3>
|
||||
warnings.warn('loading ' + DB_NAME)
|
||||
for collection, rec_list in raw_data['Schedule'].items(): # <3>
|
||||
record_type = collection[:-1] # <4>
|
||||
for collection, rec_list in raw_data['Schedule'].items(): # <4>
|
||||
record_type = collection[:-1] # <5>
|
||||
for record in rec_list:
|
||||
key = '{}.{}'.format(record_type, record['serial']) # <5>
|
||||
record['serial'] = key # <6>
|
||||
db[key] = Record(**record) # <7>
|
||||
key = '{}.{}'.format(record_type, record['serial']) # <6>
|
||||
record['serial'] = key # <7>
|
||||
db[key] = Record(**record) # <8>
|
||||
|
||||
# END SCHEDULE1
|
||||
|
||||
@@ -56,15 +56,15 @@ class MissingDatabaseError(RuntimeError):
|
||||
|
||||
class DbRecord(Record): # <2>
|
||||
|
||||
_db = None # <3>
|
||||
__db = None # <3>
|
||||
|
||||
@staticmethod # <4>
|
||||
def set_db(db):
|
||||
DbRecord._db = db # <5>
|
||||
DbRecord.__db = db # <5>
|
||||
|
||||
@staticmethod # <6>
|
||||
def get_db():
|
||||
return DbRecord._db
|
||||
return DbRecord.__db
|
||||
|
||||
@classmethod # <7>
|
||||
def fetch(cls, ident):
|
||||
@@ -93,22 +93,23 @@ class Event(DbRecord): # <1>
|
||||
@property
|
||||
def venue(self):
|
||||
key = 'venue.{}'.format(self.venue_serial)
|
||||
return self.fetch(key) # <2>
|
||||
return self.__class__.fetch(key) # <2>
|
||||
|
||||
@property
|
||||
def speakers(self):
|
||||
if not hasattr(self, '_speaker_objs'): # <3>
|
||||
spkr_serials = self.__dict__['speakers'] # <4>
|
||||
self._speaker_objs = [self.fetch('speaker.{}'.format(key))
|
||||
for key in spkr_serials] # <5>
|
||||
return self._speaker_objs # <6>
|
||||
fetch = self.__class__.fetch # <5>
|
||||
self._speaker_objs = [fetch('speaker.{}'.format(key))
|
||||
for key in spkr_serials] # <6>
|
||||
return self._speaker_objs # <7>
|
||||
|
||||
def __repr__(self):
|
||||
if hasattr(self, 'name'): # <7>
|
||||
if hasattr(self, 'name'): # <8>
|
||||
cls_name = self.__class__.__name__
|
||||
return '<{} {!r}>'.format(cls_name, self.name)
|
||||
else:
|
||||
return super().__repr__() # <8>
|
||||
return super().__repr__() # <9>
|
||||
# END SCHEDULE2_EVENT
|
||||
|
||||
|
||||
@@ -127,5 +128,5 @@ def load_db(db):
|
||||
for record in rec_list: # <7>
|
||||
key = '{}.{}'.format(record_type, record['serial'])
|
||||
record['serial'] = key
|
||||
db[key] = factory(**record) # <>
|
||||
db[key] = factory(**record) # <8>
|
||||
# END SCHEDULE2_LOAD
|
||||
|
||||
29
metaprog/prop_inheritance.py
Normal file
29
metaprog/prop_inheritance.py
Normal file
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
Alex Martelli, _Python in a Nutshell, 2e._ (O'Reilly, 2006), p. 101
|
||||
|
||||
==========================
|
||||
Properties and inheritance
|
||||
==========================
|
||||
|
||||
Properties are inherited normally, just like any other attribute.
|
||||
However, there’s a little trap for the unwary: the methods called
|
||||
upon to access a property are those that are defined in the class
|
||||
in which the property itself is defined, without intrinsic use of
|
||||
further overriding that may happen in subclasses. For example:
|
||||
"""
|
||||
|
||||
class B(object):
|
||||
|
||||
def f(self):
|
||||
return 23
|
||||
|
||||
g = property(f)
|
||||
|
||||
class C(B):
|
||||
|
||||
def f(self):
|
||||
return 42
|
||||
|
||||
c = C()
|
||||
|
||||
print(c.g) # prints 23, not 42
|
||||
44
metaprog/special_attrs.py
Normal file
44
metaprog/special_attrs.py
Normal file
@@ -0,0 +1,44 @@
|
||||
|
||||
'''
|
||||
4.13. Special Attributes
|
||||
|
||||
The implementation adds a few special read-only attributes to
|
||||
several object types, where they are relevant.
|
||||
|
||||
Some of these are not reported by the dir() built-in function.
|
||||
|
||||
https://docs.python.org/3/library/stdtypes.html#special-attributes
|
||||
'''
|
||||
|
||||
|
||||
obj_attrs = {'__dict__', '__class__'}
|
||||
|
||||
cls_data_attrs = {'__slots__', '__bases__', '__name__', '__qualname__', '__mro__'}
|
||||
|
||||
cls_methods = {'mro', '__subclasses__'}
|
||||
|
||||
cls_attrs = cls_data_attrs | cls_methods
|
||||
|
||||
|
||||
an_object = object()
|
||||
|
||||
class EmptyClass():
|
||||
pass
|
||||
|
||||
an_instance = EmptyClass()
|
||||
|
||||
class EmptySlots():
|
||||
__slots__ = ()
|
||||
|
||||
a_slots_instance = EmptySlots()
|
||||
|
||||
|
||||
objs = EmptyClass, EmptySlots, an_object, an_instance, a_slots_instance
|
||||
|
||||
for obj in objs:
|
||||
print('-' * 60)
|
||||
print(repr(obj), ':', type(obj))
|
||||
dir_obj = set(dir(obj))
|
||||
print('obj_attrs not listed:', sorted(obj_attrs - dir_obj))
|
||||
print('all cls_attrs :', sorted(cls_attrs))
|
||||
print('cls_attrs not listed:', sorted(cls_attrs - dir_obj))
|
||||
Reference in New Issue
Block a user