Created
December 20, 2014 12:29
-
-
Save dundee/831bdb68d30c80a0b126 to your computer and use it in GitHub Desktop.
Python descriptors and metaclass benchmark
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
from datetime import datetime | |
if sys.version_info[0] == 2: | |
range = xrange | |
class StockClassic(object): | |
def __init__(self, name, shares, price): | |
self.set_name(name) | |
self.set_shares(shares) | |
self.set_price(price) | |
def get_name(self): | |
return self._name | |
def set_name(self, name): | |
if not isinstance(name, str): | |
raise TypeError('Expected str') | |
self._name = name | |
def get_shares(self): | |
return self._shares | |
def set_shares(self, shares): | |
if not isinstance(shares, int): | |
raise TypeError('Expected int') | |
if shares < 0: | |
raise ValueError('Expected value >= 0') | |
self._shares = shares | |
def get_price(self): | |
return self._price | |
def set_price(self, price): | |
if not isinstance(price, float): | |
raise TypeError('Expected float') | |
if price < 0: | |
raise ValueError('Expected value >= 0') | |
self._price = price | |
#########################PROPERTY############## | |
class StockProperty(object): | |
def __init__(self, name, shares, price): | |
self.name = name | |
self.shares = shares | |
self.price = price | |
@property | |
def name(self): | |
return self._name | |
@name.setter | |
def name(self, name): | |
if not isinstance(name, str): | |
raise TypeError('Expected str') | |
self._name = name | |
@property | |
def shares(self): | |
return self._shares | |
@shares.setter | |
def shares(self, shares): | |
if not isinstance(shares, int): | |
raise TypeError('Expected int') | |
if shares < 0: | |
raise ValueError('Expected value >= 0') | |
self._shares = shares | |
@property | |
def price(self): | |
return self._price | |
@price.setter | |
def price(self, price): | |
if not isinstance(price, float): | |
raise TypeError('Expected float') | |
if price < 0: | |
raise ValueError('Expected value >= 0') | |
self._price = price | |
#########################DESCRIPTOR############## | |
class NaiveString(object): | |
def __init__(self, name): | |
self.name = name | |
def __set__(self, obj, value): | |
if not isinstance(value, str): | |
raise TypeError('Expected str') | |
obj.__dict__[self.name] = value | |
def __get__(self, obj, type=None): | |
return obj.__dict__.get(self.name, None) | |
class NaivePositiveInteger(object): | |
def __init__(self, name): | |
self.name = name | |
def __set__(self, obj, value): | |
if not isinstance(value, int): | |
raise TypeError('Expected int') | |
if value < 0: | |
raise ValueError('Expected value >= 0') | |
obj.__dict__[self.name] = value | |
def __get__(self, obj, type=None): | |
obj.__dict__.get(self.name, None) | |
class NaivePositiveFloat(object): | |
def __init__(self, name): | |
self.name = name | |
def __set__(self, obj, value): | |
if not isinstance(value, float): | |
raise TypeError('Expected float') | |
if value < 0: | |
raise ValueError('Expected value >= 0') | |
obj.__dict__[self.name] = value | |
def __get__(self, obj, type=None): | |
obj.__dict__.get(self.name, None) | |
class StockDescriptor(object): | |
name = NaiveString('name') | |
shares = NaivePositiveInteger('shares') | |
price = NaivePositiveFloat('price') | |
def __init__(self, name, shares, price): | |
self.name = name | |
self.shares = shares | |
self.price = price | |
#########################DESCRIPTOR HIERARCHY#### | |
class Descriptor(object): | |
def __init__(self, name=None): | |
self.name = name | |
def __set__(self, obj, value): | |
obj.__dict__[self.name] = value | |
def __get__(self, obj, type=None): | |
return obj.__dict__.get(self.name, None) | |
class Typed(Descriptor): | |
def __set__(self, obj, value): | |
if not isinstance(value, self.type): | |
raise TypeError('Expected %s' % self.type) | |
super(Typed, self).__set__(obj, value) | |
class String(Typed): | |
type = str | |
class Integer(Typed): | |
type = int | |
class Float(Typed): | |
type = float | |
class Positive(Descriptor): | |
def __set__(self, obj, value): | |
if value < 0: | |
raise ValueError('Expected value >= 0') | |
super(Positive, self).__set__(obj, value) | |
class PositiveInteger(Integer, Positive): | |
pass | |
class PositiveFloat(Float, Positive): | |
pass | |
class StockDescriptorHierarchy(object): | |
name = String('name') | |
shares = PositiveInteger('shares') | |
price = PositiveFloat('price') | |
def __init__(self, name, shares, price): | |
self.name = name | |
self.shares = shares | |
self.price = price | |
#########################DESCRIPTOR HIERARCHY WITH METACLASS#### | |
class DescriptorMeta(type): | |
def __init__(cls, classname, bases, clsdict): | |
type.__init__(cls, classname, bases, clsdict) | |
for name, item in clsdict.items(): | |
if isinstance(item, Descriptor): | |
item.name = name | |
class StockDescriptorHierarchyWithMeta(object): | |
__metaclass__ = DescriptorMeta | |
name = String() | |
shares = PositiveInteger() | |
price = PositiveFloat() | |
def __init__(self, name, shares, price): | |
self.name = name | |
self.shares = shares | |
self.price = price | |
################################################# | |
####################### BENCHMARK ############### | |
################################################# | |
repeats = 10 ** 6 | |
obj = StockClassic('GOOG', 100, 50.0) | |
start = datetime.now() | |
for i in range(repeats): | |
obj.get_name() | |
obj.get_shares() | |
obj.get_price() | |
referal_get = (datetime.now() - start).total_seconds() | |
print('Classic get:', referal_get, 1) | |
start = datetime.now() | |
for i in range(repeats): | |
obj.set_name('G') | |
obj.set_shares(50) | |
obj.set_price(20.0) | |
referal_set = (datetime.now() - start).total_seconds() | |
print('Classic set:', referal_set, 1) | |
for method in ['Property', 'Descriptor', 'DescriptorHierarchy', 'DescriptorHierarchyWithMeta']: | |
class_name = 'Stock' + method | |
obj = globals()[class_name]('GOOG', 100, 50.0) | |
start = datetime.now() | |
for i in range(repeats): | |
obj.name | |
obj.shares | |
obj.price | |
delta = (datetime.now() - start).total_seconds() | |
print(method + ' get:', delta, round(delta / referal_get, 2)) | |
start = datetime.now() | |
for i in range(repeats): | |
obj.name = 'G' | |
obj.shares = 50 | |
obj.price = 20.0 | |
delta = (datetime.now() - start).total_seconds() | |
print(method + ' set:', delta, round(delta / referal_set, 2)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment