如何用Python计算任意数量线段长度?代码报错及概念疑问
Let's fix your code step by step and break down the issues you're facing.
First, let's address the "tuple cannot be used as operand for subtraction" error
The main culprit here is how you're handling coordinates in the distance function and how you're initializing Point objects in LineString.__init__:
Wrong Point initialization:
In yourLineString.__init__, you wrote:self.args=[Point(*args) for p in args]Here,
argsis the collection of coordinate tuples (like(1,1)or(0,2)). For eachpinargs, you need to unpackp(not the entireargstuple) to create aPoint. So it should bePoint(*p).Broken distance calculation:
- Your formula has a critical typo: the distance between two points uses the sum of squared differences, not the difference of squares.
- You’re packing
p1.x, p1.yinto a tuplea, then trying to doa**2— tuples don’t support arithmetic operations like exponentiation or subtraction directly. You don’t need these tuples at all; just use thexandyattributes directly for calculations.
Next, fix missing methods and syntax issues
Your code is missing two essential methods for LineString, plus a syntax error in imports:
move: To shift all points in the LineString (you’re calling this method but haven’t implemented it)__getitem__: To allow indexing likelin1[0]orlin2[-1]- The import line is invalid: split
from itertools import starmapandimport mathinto separate lines.
Corrected Full Code
from itertools import starmap import math class Point(object): def __init__(self, x, y): self.x = x self.y = y def move(self, dx, dy): self.x += dx self.y += dy class LineString(object): def __init__(self, *args): # Correctly create Point instances from each coordinate tuple self.points = [Point(*p) for p in args] def move(self, dx, dy): # Move every point in the LineString for point in self.points: point.move(dx, dy) def __getitem__(self, key): # Allow indexing to access specific points (e.g., lin1[0], lin2[-1]) return self.points[key] def length(self): # Pair each point with the next consecutive point point_pairs = zip(self.points, self.points[1:]) return sum(starmap(distance, point_pairs)) def distance(p1, p2): # Correct distance formula using direct attribute access dx = p1.x - p2.x dy = p1.y - p2.y return math.sqrt(dx**2 + dy**2) if __name__ == '__main__': # Tests for LineString # =================================== lin1 = LineString((1, 1), (0, 2)) assert lin1.length() == math.sqrt(2.0) lin1.move(-1, -1) assert lin1[0].y == 0 lin2 = LineString((1, 1), (1, 2), (2, 2)) assert lin2.length() == 2.0 lin2.move(-1, -1) assert lin2.length() == 2.0 assert lin2[-1].x == 1 print('Success! Line tests passed!')
Key Python Concepts You Might Have Missed
Let’s break down the core ideas that were tripping you up:
- Variable-length arguments (
*args): When you define a method with*args, it captures all positional arguments as a tuple. InLineString.__init__,argsholds your coordinate tuples — you need to iterate over each tuple and unpack it to createPointinstances. - Special "dunder" methods:
__getitem__is a special method that lets your class support indexing (likemy_obj[0]), making it behave like built-in types such as lists or tuples. - Tuple limitations: Tuples are immutable sequences and don’t support arithmetic operations. If you need to do math on coordinate values, access individual numeric attributes instead of packing them into tuples.
- Method scope: When you call
lin1.move(), you need to implement this method inLineStringto iterate over all its points and call eachPoint’smovemethod — methods on parent objects don’t automatically propagate to child attributes.
内容的提问来源于stack exchange,提问作者Kaleab Woldemariam

