dynamic attributes, descriptors and first concurrency examples

This commit is contained in:
Luciano Ramalho
2015-01-17 22:40:40 -02:00
parent 0618105a47
commit dd1a53ff71
27 changed files with 1151 additions and 216 deletions

View File

@@ -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 @@

View 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)

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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, theres 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
View 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))