__getattr__ 实施 __setattr__ 时抛出最大递归错误
__getattr__ throwing maximum recursion error when __setattr__ implemented
我正在尝试使用魔术方法 __getattr__
和 __setattr__
创建一个圆圈 class,而且我的 __getattr__
似乎可以正常工作,但是当我实现 __setattr__
(如果值为 int,则应仅允许设置 x
和 y
的值,并在用户尝试设置时引发 AttributeError
属性 area
、circumference
和 distance
到 circle
),我的 __getattr__
抛出最大递归错误。当我将其注释掉时,__getattr__
就可以正常工作了。
from math import pi, hypot, sqrt
'''
Circle class using __getattr__, and __setattr__ (rename circle2)
'''
# __getattr__(self, name): Automatically called when the attribute name
# is accessed and the object has no such attribute.
# __setattr__(self, name, value): Automatically called when an attempt is made to bind the attribute name to value.
class Circle:
def __init__(self, x, y, r):
self.x = x
self.y = y
self.r = r
self.area = pi * self.r * self.r
self.circumference = 2 * pi * self.r
self.distance_to_origin = abs(sqrt((self.x - 0)*(self.x - 0) + (self.y - 0) * (self.y - 0)) - self.r)
def __getattr__(self, name):
if name in ["x", "y", "r", "area", "circumference", "distance_to_origin"]:
print('__get if statement') # check getattr working
return getattr(self, name)
else:
print('Not an attribute')
return None
'''
def __setattr__(self, name, value):
print(name, value)
if name in ['x', 'y']:
if isinstance(value, int):
print('we can set x,y')
self.__dict__[name] = value
else: # value isn't an int
raise TypeError('Expected an int')
elif name in ['area', 'circumference', 'distance_to_origin']:
raise RuntimeError('Cannot set attribute')
'''
if __name__ == '__main__':
circle = Circle(x=3, y=4, r=5)
# print(circle.x)
print(circle.__getattr__('x'))
# print(circle.y)
print(circle.__getattr__('y'))
# print(circle.r)
print(circle.__getattr__('r'))
# print(circle.area)
print(circle.__getattr__('area'))
# print(circle.circumference)
print(circle.__getattr__('circumference'))
# print(circle.distance_to_origin)
print(circle.__getattr__('distance_to_origin'))
# print(circle.test)
'''
tests = [('circle.x = 12.3', "print('Setting circle.x to non-integer fails')"),
('circle.y = 23.4', "print('Setting circle.y to non-integer fails')"),
('circle.area = 23.4', "print('Setting circle.area fails')"),
('circle.circumference = 23.4', "print('Setting circle.circumference fails')"),
('circle.distance_to_origin = 23.4', "print('Setting circle.distance_to_origin fails')"),
('circle.z = 5.6', "print('Setting circle.z fails')"),
('print(circle.z)', "print('Printing circle.z fails')")]
for test in tests:
try:
exec(test[0])
except:
exec(test[1])
'''
注释掉__setattr__
,测试代码:
if __name__ == '__main__':
circle = Circle(x=3, y=4, r=5)
# print(circle.x)
print(circle.__getattr__('x'))
# print(circle.y)
print(circle.__getattr__('y'))
# print(circle.r)
print(circle.__getattr__('r'))
# print(circle.area)
print(circle.__getattr__('area'))
# print(circle.circumference)
print(circle.__getattr__('circumference'))
# print(circle.distance_to_origin)
print(circle.__getattr__('distance_to_origin'))
打印出来:
__get if statement
3
__get if statement
4
__get if statement
5
__get if statement
78.53981633974483
__get if statement
31.41592653589793
__get if statement
0.0
改进的解决方案
根据此处的讨论,这是一个更短的改进版本。实现与原方案相同:
from math import pi, hypot, sqrt
class Circle:
def __init__(self, x, y, r):
self.x = x
self.y = y
super().__setattr__('r', r)
super().__setattr__('area', pi * self.r * self.r)
super().__setattr__('circumference', 2 * pi * self.r)
super().__setattr__('distance_to_origin',
abs(sqrt(self.x * self.x + self.y * self.y) - self.r))
def __setattr__(self, name, value):
if name in ['x', 'y']:
if isinstance(value, int):
print('we can set x,y')
super().__setattr__(name, value)
else: # value isn't an int
raise TypeError('Expected an int for: {}'.format(name))
else:
raise AttributeError('Cannot set attribute: {}'.format(name))
解决方案
一起避免 __getattr__()
并使用标志 self._intialized
表示 __init__()
是否已经 运行 会起作用:
from math import pi, hypot, sqrt
'''
Circle class using __getattr__, and __setattr__ (rename circle2)
'''
# __getattr__(self, name): Automatically called when the attribute name
# is accessed and the object has no such attribute.
# __setattr__(self, name, value): Automatically called when an attempt is made to bind the attribute name to value.
class Circle:
def __init__(self, x, y, r):
self._intialized = False
self.x = x
self.y = y
self.r = r
self.area = pi * self.r * self.r
self.circumference = 2 * pi * self.r
self.distance_to_origin = abs(sqrt(self.x * self.x + self.y * self.y) - self.r)
self._intialized = True
def __setattr__(self, name, value):
if name in ['_intialized']:
self.__dict__[name] = value
return
if name in ['x', 'y']:
if isinstance(value, int):
print('we can set x,y')
self.__dict__[name] = value
else: # value isn't an int
raise TypeError('Expected an int for: {}'.format(name))
elif not self._intialized:
self.__dict__[name] = value
elif name in ['area', 'circumference', 'distance_to_origin']:
raise AttributeError('Cannot set attribute: {}'.format(name))
if __name__ == '__main__':
circle = Circle(x=3, y=4, r=5)
print('x:', circle.x)
print('y:', circle.y)
print('r:', circle.r)
print('area:', circle.area)
print('circumference:', circle.circumference)
print('distance_to_origin:', circle.distance_to_origin)
tests = [('circle.x = 12.3', "print('Setting circle.x to non-integer fails')"),
('circle.y = 23.4', "print('Setting circle.y to non-integer fails')"),
('circle.area = 23.4', "print('Setting circle.area fails')"),
('circle.circumference = 23.4', "print('Setting circle.circumference fails')"),
('circle.distance_to_origin = 23.4', "print('Setting circle.distance_to_origin fails')"),
('circle.z = 5.6', "print('Setting circle.z fails')"),
('print(circle.z)', "print('Printing circle.z fails')")]
for test in tests:
try:
exec(test[0])
except:
exec(test[1])
输出看起来不错:
python get_set_attr.py
we can set x,y
we can set x,y
x: 3
y: 4
r: 5
area: 78.53981633974483
circumference: 31.41592653589793
distance_to_origin: 0.0
Setting circle.x to non-integer fails
Setting circle.y to non-integer fails
Setting circle.area fails
Setting circle.circumference fails
Setting circle.distance_to_origin fails
Printing circle.z fails
变化
这将允许使用任何其他名称设置属性:
circle.xyz = 100
但它不会在那里:
circle.xyz
Traceback (most recent call last):
File "get_set_attr.py", line 62, in <module>
circle.xyz
AttributeError: 'Circle' object has no attribute 'xyz'
__setattr__
的这种实现将避免这种情况:
def __setattr__(self, name, value):
if name in ['_intialized']:
self.__dict__[name] = value
return
if name in ['x', 'y']:
if isinstance(value, int):
print('we can set x,y')
self.__dict__[name] = value
return
else: # value isn't an int
raise TypeError('Expected an int for: {}'.format(name))
elif not self._intialized:
self.__dict__[name] = value
else:
raise AttributeError('Cannot set attribute: {}'.format(name))
什么时候使用__getattr__()
?
当您访问不存在的属性时,Python 引发 AttributeError
:
class A:
pass
a = A()
a.xyz
....
AttributeError: 'A' object has no attribute 'xyz'
Python 仅当属性 不 存在时才调用 __getattr__()
。
一个用例是另一个对象的包装器而不是使用继承。
例如,我们可以定义一个 ListWrapper
使用列表但只允许 white-listed 属性:
class ListWrapper:
_allowed_attrs = set(['append', 'extend'])
def __init__(self, value=None):
self._wrapped = list(value) if value is not None else []
def __getattr__(self, name):
if name in self._allowed_attrs:
return getattr(self._wrapped, name)
else:
raise AttributeError('No attribute {}.'.format(name))
def __repr__(self):
return repr(self._wrapped)
我们可以像列表一样使用它:
>>> my_list = ListWrapper('abc')
>>> my_list
['a', 'b', 'c']
追加元素:
>>> my_list.append('x')
>>> my_list
['a', 'b', 'c', 'x']
但我们不能使用除 _allowed_attrs
中定义的属性之外的任何其他属性:
my_list.index('a')
...
AttributeError: No attribute index.
docs 说的是什么:
object.__getattr__(self, name)
Called when an attribute lookup has not found the attribute in the usual places (i.e. it is not an instance attribute nor is it found in the class tree for self). name is the attribute name. This method should return the (computed) attribute value or raise an AttributeError
exception.
Note that if the attribute is found through the normal mechanism, __getattr__()
is not called. (This is an intentional asymmetry between __getattr__()
and __setattr__()
.) This is done both for efficiency reasons and because otherwise __getattr__()
would have no way to access other attributes of the instance. Note that at least for instance variables, you can fake total control by not inserting any values in the instance attribute dictionary (but instead inserting them in another object). See the __getattribute__()
method below for a way to actually get total control over attribute access.
您可能对导致问题的代码中的几个问题感兴趣。
您不能直接在 __init__()
中设置以下内容,因为赋值会触发对 __setattr__()
的调用,它只会设置 x
和 y
。因此,从未设置这些属性。
self.r = r
self.area = pi * self.r * self.r
self.circumference = 2 * pi * self.r
self.distance_to_origin = abs(sqrt((self.x - 0)*(self.x - 0) + (self.y - 0) * (self.y - 0)) - self.r)
您没有在 __setattr__()
中检查 r
。这导致 r
被静默忽略,然后当 r
被访问以在 __init__()
中设置 area
时, __getattr__()
调用 getattr()
调用 __getattr__()
调用了getattr()
等等(因为没有设置r
),导致递归。
elif name in ['area', 'circumference', 'distance_to_origin']:
raise RuntimeError('Cannot set attribute')
这里是固定代码。更改已在下面的评论中用 mod
标记。
#!/usr/bin/python3
from math import pi, hypot, sqrt
'''
Circle class using __getattr__, and __setattr__ (rename circle2)
'''
# __getattr__(self, name): Automatically called when the attribute name
# is accessed and the object has no such attribute.
# __setattr__(self, name, value): Automatically called when an attempt is made to bind the attribute name to value.
class Circle:
def __init__(self, x, y, r):
self.x = x
self.y = y
# mod : can't set via self.__getattr__
super().__setattr__("r", r)
super().__setattr__("area", pi * self.r * self.r)
super().__setattr__("circumference", 2 * pi * self.r)
super().__setattr__("distance_to_origin", abs(sqrt((self.x - 0)*(self.x - 0) + (self.y - 0) * (self.y - 0)) - self.r))
def __getattr__(self, name):
print("===== get:", name)
if name in ["x", "y", "r", "area", "circumference", "distance_to_origin"]:
print('__get if statement') # check getattr working
return getattr(self, name)
else:
print('Not an attribute')
return None
def __setattr__(self, name, value):
print("===== set:", name, value)
if name in ['x', 'y']:
if isinstance(value, int):
print('we can set x,y')
super().__setattr__(name, value) # mod : better
else: # value isn't an int
raise TypeError('Expected an int')
elif name in ['r', 'area', 'circumference', 'distance_to_origin']: # mod : add 'r'
raise RuntimeError('Cannot set attribute')
if __name__ == '__main__':
circle = Circle(x=3, y=4, r=5)
# print(circle.x)
print(circle.__getattr__('x'))
# print(circle.y)
print(circle.__getattr__('y'))
# print(circle.r)
print(circle.__getattr__('r'))
# print(circle.area)
print(circle.__getattr__('area'))
# print(circle.circumference)
print(circle.__getattr__('circumference'))
# print(circle.distance_to_origin)
print(circle.__getattr__('distance_to_origin'))
# print(circle.test)
'''
tests = [('circle.x = 12.3', "print('Setting circle.x to non-integer fails')"),
('circle.y = 23.4', "print('Setting circle.y to non-integer fails')"),
('circle.area = 23.4', "print('Setting circle.area fails')"),
('circle.circumference = 23.4', "print('Setting circle.circumference fails')"),
('circle.distance_to_origin = 23.4', "print('Setting circle.distance_to_origin fails')"),
('circle.z = 5.6', "print('Setting circle.z fails')"),
('print(circle.z)', "print('Printing circle.z fails')")]
for test in tests:
try:
exec(test[0])
except:
exec(test[1])
'''
我正在尝试使用魔术方法 __getattr__
和 __setattr__
创建一个圆圈 class,而且我的 __getattr__
似乎可以正常工作,但是当我实现 __setattr__
(如果值为 int,则应仅允许设置 x
和 y
的值,并在用户尝试设置时引发 AttributeError
属性 area
、circumference
和 distance
到 circle
),我的 __getattr__
抛出最大递归错误。当我将其注释掉时,__getattr__
就可以正常工作了。
from math import pi, hypot, sqrt
'''
Circle class using __getattr__, and __setattr__ (rename circle2)
'''
# __getattr__(self, name): Automatically called when the attribute name
# is accessed and the object has no such attribute.
# __setattr__(self, name, value): Automatically called when an attempt is made to bind the attribute name to value.
class Circle:
def __init__(self, x, y, r):
self.x = x
self.y = y
self.r = r
self.area = pi * self.r * self.r
self.circumference = 2 * pi * self.r
self.distance_to_origin = abs(sqrt((self.x - 0)*(self.x - 0) + (self.y - 0) * (self.y - 0)) - self.r)
def __getattr__(self, name):
if name in ["x", "y", "r", "area", "circumference", "distance_to_origin"]:
print('__get if statement') # check getattr working
return getattr(self, name)
else:
print('Not an attribute')
return None
'''
def __setattr__(self, name, value):
print(name, value)
if name in ['x', 'y']:
if isinstance(value, int):
print('we can set x,y')
self.__dict__[name] = value
else: # value isn't an int
raise TypeError('Expected an int')
elif name in ['area', 'circumference', 'distance_to_origin']:
raise RuntimeError('Cannot set attribute')
'''
if __name__ == '__main__':
circle = Circle(x=3, y=4, r=5)
# print(circle.x)
print(circle.__getattr__('x'))
# print(circle.y)
print(circle.__getattr__('y'))
# print(circle.r)
print(circle.__getattr__('r'))
# print(circle.area)
print(circle.__getattr__('area'))
# print(circle.circumference)
print(circle.__getattr__('circumference'))
# print(circle.distance_to_origin)
print(circle.__getattr__('distance_to_origin'))
# print(circle.test)
'''
tests = [('circle.x = 12.3', "print('Setting circle.x to non-integer fails')"),
('circle.y = 23.4', "print('Setting circle.y to non-integer fails')"),
('circle.area = 23.4', "print('Setting circle.area fails')"),
('circle.circumference = 23.4', "print('Setting circle.circumference fails')"),
('circle.distance_to_origin = 23.4', "print('Setting circle.distance_to_origin fails')"),
('circle.z = 5.6', "print('Setting circle.z fails')"),
('print(circle.z)', "print('Printing circle.z fails')")]
for test in tests:
try:
exec(test[0])
except:
exec(test[1])
'''
注释掉__setattr__
,测试代码:
if __name__ == '__main__':
circle = Circle(x=3, y=4, r=5)
# print(circle.x)
print(circle.__getattr__('x'))
# print(circle.y)
print(circle.__getattr__('y'))
# print(circle.r)
print(circle.__getattr__('r'))
# print(circle.area)
print(circle.__getattr__('area'))
# print(circle.circumference)
print(circle.__getattr__('circumference'))
# print(circle.distance_to_origin)
print(circle.__getattr__('distance_to_origin'))
打印出来:
__get if statement
3
__get if statement
4
__get if statement
5
__get if statement
78.53981633974483
__get if statement
31.41592653589793
__get if statement
0.0
改进的解决方案
根据此处的讨论,这是一个更短的改进版本。实现与原方案相同:
from math import pi, hypot, sqrt
class Circle:
def __init__(self, x, y, r):
self.x = x
self.y = y
super().__setattr__('r', r)
super().__setattr__('area', pi * self.r * self.r)
super().__setattr__('circumference', 2 * pi * self.r)
super().__setattr__('distance_to_origin',
abs(sqrt(self.x * self.x + self.y * self.y) - self.r))
def __setattr__(self, name, value):
if name in ['x', 'y']:
if isinstance(value, int):
print('we can set x,y')
super().__setattr__(name, value)
else: # value isn't an int
raise TypeError('Expected an int for: {}'.format(name))
else:
raise AttributeError('Cannot set attribute: {}'.format(name))
解决方案
一起避免 __getattr__()
并使用标志 self._intialized
表示 __init__()
是否已经 运行 会起作用:
from math import pi, hypot, sqrt
'''
Circle class using __getattr__, and __setattr__ (rename circle2)
'''
# __getattr__(self, name): Automatically called when the attribute name
# is accessed and the object has no such attribute.
# __setattr__(self, name, value): Automatically called when an attempt is made to bind the attribute name to value.
class Circle:
def __init__(self, x, y, r):
self._intialized = False
self.x = x
self.y = y
self.r = r
self.area = pi * self.r * self.r
self.circumference = 2 * pi * self.r
self.distance_to_origin = abs(sqrt(self.x * self.x + self.y * self.y) - self.r)
self._intialized = True
def __setattr__(self, name, value):
if name in ['_intialized']:
self.__dict__[name] = value
return
if name in ['x', 'y']:
if isinstance(value, int):
print('we can set x,y')
self.__dict__[name] = value
else: # value isn't an int
raise TypeError('Expected an int for: {}'.format(name))
elif not self._intialized:
self.__dict__[name] = value
elif name in ['area', 'circumference', 'distance_to_origin']:
raise AttributeError('Cannot set attribute: {}'.format(name))
if __name__ == '__main__':
circle = Circle(x=3, y=4, r=5)
print('x:', circle.x)
print('y:', circle.y)
print('r:', circle.r)
print('area:', circle.area)
print('circumference:', circle.circumference)
print('distance_to_origin:', circle.distance_to_origin)
tests = [('circle.x = 12.3', "print('Setting circle.x to non-integer fails')"),
('circle.y = 23.4', "print('Setting circle.y to non-integer fails')"),
('circle.area = 23.4', "print('Setting circle.area fails')"),
('circle.circumference = 23.4', "print('Setting circle.circumference fails')"),
('circle.distance_to_origin = 23.4', "print('Setting circle.distance_to_origin fails')"),
('circle.z = 5.6', "print('Setting circle.z fails')"),
('print(circle.z)', "print('Printing circle.z fails')")]
for test in tests:
try:
exec(test[0])
except:
exec(test[1])
输出看起来不错:
python get_set_attr.py
we can set x,y
we can set x,y
x: 3
y: 4
r: 5
area: 78.53981633974483
circumference: 31.41592653589793
distance_to_origin: 0.0
Setting circle.x to non-integer fails
Setting circle.y to non-integer fails
Setting circle.area fails
Setting circle.circumference fails
Setting circle.distance_to_origin fails
Printing circle.z fails
变化
这将允许使用任何其他名称设置属性:
circle.xyz = 100
但它不会在那里:
circle.xyz
Traceback (most recent call last):
File "get_set_attr.py", line 62, in <module>
circle.xyz
AttributeError: 'Circle' object has no attribute 'xyz'
__setattr__
的这种实现将避免这种情况:
def __setattr__(self, name, value):
if name in ['_intialized']:
self.__dict__[name] = value
return
if name in ['x', 'y']:
if isinstance(value, int):
print('we can set x,y')
self.__dict__[name] = value
return
else: # value isn't an int
raise TypeError('Expected an int for: {}'.format(name))
elif not self._intialized:
self.__dict__[name] = value
else:
raise AttributeError('Cannot set attribute: {}'.format(name))
什么时候使用__getattr__()
?
当您访问不存在的属性时,Python 引发 AttributeError
:
class A:
pass
a = A()
a.xyz
....
AttributeError: 'A' object has no attribute 'xyz'
Python 仅当属性 不 存在时才调用 __getattr__()
。
一个用例是另一个对象的包装器而不是使用继承。
例如,我们可以定义一个 ListWrapper
使用列表但只允许 white-listed 属性:
class ListWrapper:
_allowed_attrs = set(['append', 'extend'])
def __init__(self, value=None):
self._wrapped = list(value) if value is not None else []
def __getattr__(self, name):
if name in self._allowed_attrs:
return getattr(self._wrapped, name)
else:
raise AttributeError('No attribute {}.'.format(name))
def __repr__(self):
return repr(self._wrapped)
我们可以像列表一样使用它:
>>> my_list = ListWrapper('abc')
>>> my_list
['a', 'b', 'c']
追加元素:
>>> my_list.append('x')
>>> my_list
['a', 'b', 'c', 'x']
但我们不能使用除 _allowed_attrs
中定义的属性之外的任何其他属性:
my_list.index('a')
...
AttributeError: No attribute index.
docs 说的是什么:
object.__getattr__(self, name)
Called when an attribute lookup has not found the attribute in the usual places (i.e. it is not an instance attribute nor is it found in the class tree for self). name is the attribute name. This method should return the (computed) attribute value or raise an
AttributeError
exception.Note that if the attribute is found through the normal mechanism,
__getattr__()
is not called. (This is an intentional asymmetry between__getattr__()
and__setattr__()
.) This is done both for efficiency reasons and because otherwise__getattr__()
would have no way to access other attributes of the instance. Note that at least for instance variables, you can fake total control by not inserting any values in the instance attribute dictionary (but instead inserting them in another object). See the__getattribute__()
method below for a way to actually get total control over attribute access.
您可能对导致问题的代码中的几个问题感兴趣。
您不能直接在 __init__()
中设置以下内容,因为赋值会触发对 __setattr__()
的调用,它只会设置 x
和 y
。因此,从未设置这些属性。
self.r = r
self.area = pi * self.r * self.r
self.circumference = 2 * pi * self.r
self.distance_to_origin = abs(sqrt((self.x - 0)*(self.x - 0) + (self.y - 0) * (self.y - 0)) - self.r)
您没有在 __setattr__()
中检查 r
。这导致 r
被静默忽略,然后当 r
被访问以在 __init__()
中设置 area
时, __getattr__()
调用 getattr()
调用 __getattr__()
调用了getattr()
等等(因为没有设置r
),导致递归。
elif name in ['area', 'circumference', 'distance_to_origin']:
raise RuntimeError('Cannot set attribute')
这里是固定代码。更改已在下面的评论中用 mod
标记。
#!/usr/bin/python3
from math import pi, hypot, sqrt
'''
Circle class using __getattr__, and __setattr__ (rename circle2)
'''
# __getattr__(self, name): Automatically called when the attribute name
# is accessed and the object has no such attribute.
# __setattr__(self, name, value): Automatically called when an attempt is made to bind the attribute name to value.
class Circle:
def __init__(self, x, y, r):
self.x = x
self.y = y
# mod : can't set via self.__getattr__
super().__setattr__("r", r)
super().__setattr__("area", pi * self.r * self.r)
super().__setattr__("circumference", 2 * pi * self.r)
super().__setattr__("distance_to_origin", abs(sqrt((self.x - 0)*(self.x - 0) + (self.y - 0) * (self.y - 0)) - self.r))
def __getattr__(self, name):
print("===== get:", name)
if name in ["x", "y", "r", "area", "circumference", "distance_to_origin"]:
print('__get if statement') # check getattr working
return getattr(self, name)
else:
print('Not an attribute')
return None
def __setattr__(self, name, value):
print("===== set:", name, value)
if name in ['x', 'y']:
if isinstance(value, int):
print('we can set x,y')
super().__setattr__(name, value) # mod : better
else: # value isn't an int
raise TypeError('Expected an int')
elif name in ['r', 'area', 'circumference', 'distance_to_origin']: # mod : add 'r'
raise RuntimeError('Cannot set attribute')
if __name__ == '__main__':
circle = Circle(x=3, y=4, r=5)
# print(circle.x)
print(circle.__getattr__('x'))
# print(circle.y)
print(circle.__getattr__('y'))
# print(circle.r)
print(circle.__getattr__('r'))
# print(circle.area)
print(circle.__getattr__('area'))
# print(circle.circumference)
print(circle.__getattr__('circumference'))
# print(circle.distance_to_origin)
print(circle.__getattr__('distance_to_origin'))
# print(circle.test)
'''
tests = [('circle.x = 12.3', "print('Setting circle.x to non-integer fails')"),
('circle.y = 23.4', "print('Setting circle.y to non-integer fails')"),
('circle.area = 23.4', "print('Setting circle.area fails')"),
('circle.circumference = 23.4', "print('Setting circle.circumference fails')"),
('circle.distance_to_origin = 23.4', "print('Setting circle.distance_to_origin fails')"),
('circle.z = 5.6', "print('Setting circle.z fails')"),
('print(circle.z)', "print('Printing circle.z fails')")]
for test in tests:
try:
exec(test[0])
except:
exec(test[1])
'''