__getattr__/__setattr__ - infinite recursion
Charles G Waldman
cgw at fnal.gov
Mon Mar 27 12:56:03 EST 2000
There are several problems in the script you've posted, below find a
corrected version with the changed lines marked with ##Change
Here's an explanation of the changes:
Change 1: You raise "Error" but don't define it anywhere. You must
create it either as a string, or as an Exception object.
Change 2: You had "self.reset_to_defaults(self)", one self too many.
Change 3: This is the main point. You have no way to init your
class, because setting the initial attributes - "attrs",
"values" and "keyname" - goes through the validation
mechanism, which doesn't make sense for these attributes,
since the validation mechanism can't be used until
these attributes are set up.
In general, if you have a setattr hook and need to set
some instance attributes while avoiding this hook, you can do
self.__dict__[key] = value, instead of self.key = value
Change 4: I just hate to see "except:" in code... you should always
name the exceptions you're looking for, if at all possible.
Otherwise debugging becomes impossible - you have a
mismatched paren or something, instead of getting a
SyntaxError you fall into the unqualified "except:" clause.
#---------------------------------------------------------------------
# Why does this blow up Python?
#---------------------------------------------------------------------
import os, sys, string, pprint
# "type objects"
int_type = type(0)
string_type = type("X")
float_type = type(0.1)
# required field flags?
required = 1
optional = 0
Error = "Error" ### Change 1
# Class with strict type checking and a single key:
class zdb_rec:
def __init__(self, pattern_dict=None, keyname=None):
"create an object with a strict pattern of required values to make up a type"
self.set_valid(pattern_dict)
self.keyname = keyname
self.reset_to_defaults() #####Change 2
def set_valid(self,pattern_dict):
self.attrs = pattern_dict
def set_keyname(self,keyname):
self.keyname = keyname
def reset_to_defaults(self):
# fill in default values for each attribute
self.values = {}
for x in self.attrs.keys():
self.values[x] = self.attrs[x][2]
def keyname(self):
return self.keyname
def key(self):
return self.values[self.keyname]
# setattr does typesafety check:
def __setattr__(self,name,value):
"custom __setattr_ with typesafety check"
if name in ('attrs','keyname','values'): ##Change 3
self.__dict__[name]=value ##Change 3
return ##Change 3
try:
if (value == None) and (not self.attrs[name][1]):
self.values[name] = None
else:
if (type(value) == self.attrs[name][1]):
self.values[name] = value
else:
raise Error, "Invalid type in assignment"+`type(value)`
except KeyError: ######Change 4
raise Error, "Invalid attribute "+`name`
def __getattr__(self,name):
try:
return self.values[name]
except KeyError: ######Change 4
raise Error, "Invalid attribute "+`name`
def attr_count(self):
return len(self.attrs)
def validate(self):
"raises an exception if validation fails, ie requird field not provided"
for x in self.attrs.keys():
if self.attrs[x][1]:
if self.values[x] == None:
raise Error, "validate() failed - required field not set"
# "Person Database Schema":
person_attributes = {
# attribute type required? initialvalue
"name": (string_type, required, ""),
"address": (string_type, required, ""),
"age": (int_type, required, 0 ),
}
x = zdb_rec(person_attributes,"name")
print "done"
More information about the Python-list
mailing list