This code is from python cook book 3rd edition from classes chapter section 8.13 . The program tries to define various kinds of data structures, but want to enforce constraints on the values that are allowed to be assigned to certain attributes. I am executing the program in python 2.7 with Pycharm IDE.
# Base class. Uses a descriptor to set a value
class Descriptor(object):
def __init__(self, name=None, **opts):
self.name = name
for key, value in opts.items():
setattr(self, key, value)
def __set__(self, instance, value):
instance.__dict__[self.name] = value
# Descriptor for enforcing types
class Typed(Descriptor):
expected_type = type(None)
def __set__(self, instance, value):
if not isinstance(value, self.expected_type):
raise TypeError('expected ' + str(self.expected_type))
super(Typed,self).__set__(instance, value)
class Integer(Typed):
expected_type = int
class String(Typed):
expected_type = str
class MaxSized(Descriptor):
def __init__(self, name=None, **opts):
if 'size' not in opts:
raise TypeError('missing size option')
super(MaxSized,self).__init__(name, **opts)
def __set__(self, instance, value):
if len(value) >= self.size:
raise ValueError('size must be < ' + str(self.size))
super(MaxSized,self).__set__(instance, value)
class SizedString(String, MaxSized):
pass
# Class decorator to apply constraints
def check_attributes(**kwargs):
def decorate(cls):
for key, value in kwargs.items():
if isinstance(value, Descriptor):
value.name = key
setattr(cls, key, value)
else:
setattr(cls, key, value(key))
return cls
return decorate
# Example
@check_attributes(name=String,shares=Integer,place=SizedString('tester',size=8))
class Stock(object):
def __init__(self, stkname, stkqty,stkhq):
self.name = stkname
self.shares = stkqty
self.place = stkhq
when executing the code with the below initialization ,
s = Stock('ACME', 50,'hky')
print s.name # print ACME
print s.shares # prints 50
print s.place # prints hky
Condition:
When debugging the below code for @check_attributes place=SizedString('tester',size=8), the below if condition is True where as for name=String and shares=Integer , the else condition is True.
if isinstance(value, Descriptor):
value.name = key
setattr(cls, key, value)
else:
setattr(cls, key, value(key))
Questions :
If SizedString is an instance of Descriptor ( based on Inheritance hierarchy- String , Typed , MaxSized, Descriptor ), then String and Integer also should satisfy the If condition right ? because at the end it is also the subclass of ( typed , Descriptor ) ?
Sorry for the lengthy context, but wanted to be clear as much as possible .
If SizedString is an instance of Descriptor ( based on Inheritance hierarchy- String , Typed , MaxSized, Descriptor ), then String and Integer also should satisfy the If condition right ? because at the end it is also the subclass of ( typed , Descriptor ) ?
We have to look carefully at what is being passed into the check_attributes
function. Look closer at what the value of the name
and share
keyword arguments are:
@check_attributes(name=String,shares=Integer,place=SizedString('tester',size=8))
Notice the lack of parenthesis after the String
and Integer
class names? This means the String
and Integer
class object themselves are being passed into check_attributes
, not an instance of either class. And since the String
class object and the Integer
class object are not sub-classes of Descriptor
, isinstance(value, Descriptor)
fails.
What is value(key) in setattr(cls, key, value(key)) means , cant understand what is value(key ) means ?
Think about it. Since value
has the value of whatever keyword argument was passed into check_attributes
, and the value is not an instance of the Descriptor
class, then value
must be referring to a class object. (If you don't understand why this is, refer back to my answer to your first question). So calling value(key)
is creating an instance of some class, and passing in the key
value as a constructor argument.