我试图计算两条线的交点,但经常得到" TypeError:' Vector'对象不可迭代" 我试图寻找答案但失败了。我该如何解决这个问题,请问?
from decimal import Decimal, getcontext
from vector import Vector
getcontext().prec = 30
class Line(object):
NO_NONZERO_ELTS_FOUND_MSG = 'No nonzero elements found'
def __init__(self, normal_vector=None, constant_term=None):
self.dimension = 2
if not normal_vector:
all_zeros = ['0']*self.dimension
normal_vector = Vector(all_zeros)
self.normal_vector = normal_vector
if not constant_term:
constant_term = Decimal('0')
self.constant_term = Decimal(constant_term)
self.set_basepoint()
def __eq__(self, ell):
if self.normal_vector.is_zero():
if not ell.normal_vector.is_zero():
return False
else:
diff = self.constant_term - ell.constant_term
return MyDecimal(diff).is_near_zero()
elif ell.normal_vector.is_zero():
return False
if not self.is_parallel_to(ell):
return False
x0 = self.basepoint
y0 = ell.basepoint
basepoint_difference = x0.minus(y0)
n = self.normal_vector
return basepoint_difference.is_orthogonal_to(n)
def is_parallel_to(self, ell):
n1 = self.normal_vector
n2 = ell.normal_vector
return n1.is_parallel_to(n2)
def set_basepoint(self):
try:
n = self.normal_vector
c = self.constant_term
basepoint_coords = ['0']*self.dimension
initial_index = Line.first_nonzero_index(n)
initial_coefficient = n[initial_index]
basepoint_coords[initial_index] = c/initial_coefficient
self.basepoint = Vector(basepoint_coords)
except Exception as e:
if str(e) == Line.NO_NONZERO_ELTS_FOUND_MSG:
self.basepoint = None
else:
raise e
def intersection_with(self, ell):
try:
A, B = self.normal_vector.coordinates
C, D = ell.normal_vector.coordinates
k1 = self.constant_term
k2 = ell.constant_term
x_numerator = D*k1 - B*k2
y_numerator = -C*k1 + A*k2
one_over_denom = Decimal('1')/(A*D - B*C)
return Vector([x_numerator, y_numerator]).times_scalar(one_over_denom)
except ZeroDivisionError:
if self == ell:
return self
else:
return None
def __str__(self):
num_decimal_places = 3
def write_coefficient(coefficient, is_initial_term=False):
coefficient = round(coefficient, num_decimal_places)
if coefficient % 1 == 0:
coefficient = int(coefficient)
output = ''
if coefficient < 0:
output += '-'
if coefficient > 0 and not is_initial_term:
output += '+'
if not is_initial_term:
output += ' '
if abs(coefficient) != 1:
output += '{}'.format(abs(coefficient))
return output
n = self.normal_vector
try:
initial_index = Line.first_nonzero_index(n)
terms = [write_coefficient(n[i], is_initial_term=(i==initial_index)) + 'x_{}'.format(i+1)
for i in range(self.dimension) if round(n[i], num_decimal_places) != 0]
output = ' '.join(terms)
except Exception as e:
if str(e) == self.NO_NONZERO_ELTS_FOUND_MSG:
output = '0'
else:
raise e
constant = round(self.constant_term, num_decimal_places)
if constant % 1 == 0:
constant = int(constant)
output += ' = {}'.format(constant)
return output
@staticmethod
def first_nonzero_index(iterable):
for k, item in enumerate(iterable):
if not MyDecimal(item).is_near_zero():
return k
raise Exception(Line.NO_NONZERO_ELTS_FOUND_MSG)
class MyDecimal(Decimal):
def is_near_zero(self, eps=1e-10):
return abs(self) < eps
ell1 = Line(normal_vector = Vector(['4.046', '2.836']), constant_term = '1.21')
ell2 = Line(normal_vector = Vector(['10.115', '7.09']), constant_term = '3.025')
print 'intersection 1:', ell1.intersection_with(ell2)
ell1 = Line(normal_vector = Vector(['7.204', '3.182']), constant_term = '8.68')
ell2 = Line(normal_vector = Vector(['8.172', '4.114']), constant_term = '9.883')
print 'intersection 2:', ell1.intersection_with(ell2)
ell1 = Line(normal_vector = Vector(['1.182', '5.562']), constant_term ='6.744')
ell2 = Line(normal_vector = Vector(['1.773', '8.343']), constant_term = '9.525')
print 'intersection 3:', ell1.intersection_with(ell2)
=============================================== ==============
Traceback (most recent call last):
File "/Users/Tristan/Desktop/line.py", line 146, in <module>
ell1 = Line(normal_vector = Vector(['4.046', '2.836']), constant_term = '1.21')
File "/Users/Tristan/Desktop/line.py", line 24, in __init__
self.set_basepoint()
File "/Users/Tristan/Desktop/line.py", line 67, in set_basepoint
raise e
TypeError: 'Vector' object is not iterable
[Finished in 0.0s with exit code 1]
[shell_cmd: python -u "/Users/Tristan/Desktop/line.py"]
[dir: /Users/Tristan/Desktop]
[path: /usr/bin:/bin:/usr/sbin:/sbin]
=============================================== ==============
from math import sqrt, acos, pi
from decimal import Decimal, getcontext
getcontext().prec = 30
class Vector(object):
CANNOT_NORMALIZE_ZERO_VECTOR_MSG = 'cannot nomalize the zero vector.'
CANNOT_COMPUTE_ANGLE_WITH_ZERO_VECTOR_MSG = 'Cannot compute an angle\
with the zero vector.'
NO_UNIQUE_PARALLEL_COMPONENT_MSG = 'there is no unique parallel\
commponent.'
NO_UNIQUE_ORTHOGONAL_COMPONENT_MSG = 'there is no unique orthogonal\
commponent.'
def __init__(self, coordinates):
try:
if not coordinates:
raise ValueError
self.coordinates = tuple([Decimal(x) for x in coordinates])
self.dimension = len(self.coordinates)
except ValueError:
raise ValueError('The coordinates must be nonempty')
except TypeError:
raise TypeError('The coordinates must be an iterable')
def __str__(self):
return 'Vector: {}'.format(self.coordinates)
def __eq__(self, v):
return self.coordinates == v.coordinates
def plus(self, v):
new_coordinates = [x + y for x,y in zip(self.coordinates, v.coordinates)]
return Vector(new_coordinates)
def minus(self, v):
new_coordinates = [x - y for x,y in zip(self.coordinates, v.coordinates)]
return Vector(new_coordinates)
def times_scalar(self, c):
new_coordinates =[Decimal(c)*x for x in self.coordinates]
return Vector(new_coordinates)
def magnitude(self):
coordinates_squared = [x**2 for x in self.coordinates]
return Decimal(sum(coordinates_squared)).sqrt()
def normalized(self):
try:
magnitude = self.magnitude()
return self.times_scalar(Decimal('1.0')/Decimal(magnitude))
except ZeroDivisionError:
raise Exception(self.CANNOT_NORMALIZE_ZERO_VECTOR_MSG)
def dot(self, v):
return sum([x*y for x, y in zip(self.coordinates, v.coordinates)])
def angle_with(self, v, in_degrees = False):
try:
u1 = self.normalized()
u2 = v.normalized()
angle_in_radians = Decimal(acos(u1.dot(u2)))
if in_degrees:
degrees_per_radian = Decimal('180.0')/Decimal(pi)
return angle_in_radians * degrees_per_radian
else:
return angle_in_radians
except Exception as e:
if str(e) == self.CANNOT_NORMALIZE_ZERO_VECTOR_MSG:
raise Exception(self.CANNOT_COMPUTE_ANGLE_WITH_ZERO_VECTOR_MSG)
else:
raise e
def is_orthogonal_to(self, v, tolerance = 1e-10):
return abs(self.dot(v)) < tolerance
def is_parallel_to(self, v):
return (self.is_zero()
or v.is_zero() or
self.angle_with(v) == 0 or
self.angle_with(v) == pi)
def is_zero(self, tolerance = 1e-10):
return self.magnitude() < tolerance
def projection(self, v):
try:
unit_v = v.normalized()
return (unit_v).times_scalar(self.dot(unit_v))
except Exception as e:
if str(e) == self.CANNOT_NORMALIZE_ZERO_VECTOR_MSG:
raise Exception(self.NO_UNIQUE_PARALLEL_COMPONENT_MSG)
else:
raise e
def orthogonal(self, v):
try:
return self.minus(self.projection(v))
except Exception as e:
if str(e) == self.NO_UNIQUE_PARALLEL_COMPONENT_MSG:
raise Exception(self.NO_UNIQUE_ORTHOGONAL_COMPONENT_MSG)
else:
raise e
def decompose(self, v):
return str(self.projection(v)) + '+' + str(self.orthogonal(v))
def cross_products(self, v):
try:
x_1, y_1, z_1 = self.coordinates
x_2, y_2, z_2 = v.coordinates
new_coordinates = [y_1*z_2 - y_2*z_1,
-(x_1*z_2 - x_2*z_1), x_1*y_2 - x_2*y_1]
return Vector(new_coordinates)
except ValueError as e:
msg = str(e)
if msg == 'need more than 2 values to unpack':
self_embedded_in_R3 = Vector(self.coordinates + ('0',))
v_embedded_in_R3 = Vector(v.coordinates + ('0',))
return self_embedded_in_R3.cross_products(v_embedded_in_R3)
elif (msg == 'too many values to unpack' or
msg == 'need more than 1 value to unpack'):
raise Exception('only defined in two 3-dimensions')
else:
raise e
def area_of_parallelogram(self, v):
return (self.cross_products(v)).magnitude()
def area_of_triangle(self, v):
return self.area_of_parallelogram(v)/Decimal('2.0')
答案 0 :(得分:3)
错误来自set_basepoint
函数中的以下行:
TypeError: 'Vector' object is not iterable
> test.py(136)first_nonzero_index()
135 def first_nonzero_index(iterable):
--> 136 for k, item in enumerate(iterable):
137 if not MyDecimal(item).is_near_zero():
ipdb> up
> test.py(57)set_basepoint()
56
---> 57 initial_index = Line.first_nonzero_index(n)
58 initial_coefficient = n[initial_index]
表示行引发异常为initial_index = Line.first_nonzero_index(n)
。
由于set_basepoint
中过于宽泛的try / except块,隐藏了包含错误的行,我已将其注释掉以实现上述目标。
我不确定在那里想要什么,也许Line.first_nonzero_index(n.coordinates)
而不会破坏。
first_nonzero_index
中的代码正在for k, item in enumerate(iterable):
,而iterable
是Vector
,这不是可迭代的,因此是错误。
通常,尽量减少try / except块,以免隐藏实际引发异常的代码。
同样在异常处理中检查str(e)
相等是不好的做法,如果异常的字符串表示发生更改,则会中断。而是在异常类上使用isinstance
。
答案 1 :(得分:0)
def set_basepoint(self):
try:
n = self.normal_vector
# print(n) # Vector: (Decimal('4.046'), Decimal('2.836'))
c = self.constant_term
basepoint_coords = ['0']*self.dimension
initial_index = Line.first_nonzero_index(n.coordinates)
# print(initial_index) # 0
initial_coefficient = n.coordinates[initial_index]
# print(initial_coefficient) # 4.046
basepoint_coords[initial_index] = c/initial_coefficient
self.basepoint = Vector(basepoint_coords)
except Exception as e:
if str(e) == Line.NO_NONZERO_ELTS_FOUND_MSG:
self.basepoint = None
else:
raise e
解决这个问题,我们应该首先认真学习Vector类。
答案 2 :(得分:0)
由于以下原因,向量类型不可迭代
def __str__(self):
return 'Vector: {}'.format(self.coordinates)
str方法实际上存储的是字符串而不是元组。
在调用first_nonzero_index()和Line.first_nonzero_index(n.coordinates)
而不是Line.first_nonzero_index(n.coordinates)
的所有实例中,修复程序都使用initial_coefficient = n.coordinates[initial_index]
而不是initial_coefficient = n[initial_index]
。此外,还有另一个错误
在线课程
在str方法中:
try:
initial_index = Line.first_nonzero_index(n.coordinates)
terms = [write_coefficient(n[i], is_initial_term=(i==initial_index))+'x_{}'.format(i+1)
for i in range(self.dimension) if round(n.coordinates[i], num_decimal_places) != 0]
output = ' '.join(terms)
向量类也不可索引,请替换每个 n 和 n.coordinates