在Python类方法中使用递归

4 投票
2 回答
10755 浏览
提问于 2025-04-16 02:41

注意:新手警报!

我正在尝试在一个Python类的方法中使用递归,但结果不太理想。

我想创建一个汽车类,里面有一些基本属性:汽车的ID、在单车道上的位置(用一个整数表示)和速度。我有一个函数是用来返回在这辆车前面的汽车ID,也就是说,如果我们有这样的类:

class Car:
    def __init__(self, position, id, velocity):
        self.position = position
        self.id = id
        self.velocity = velocity

现在,我想出了以下的类方法(代码下面有更多细节):

def findSuccessorCar(self, cars):

    successorCar = ""
    smallestGapFound = 20000000 

    for car in cars:
        if car.id == self.id: continue

        currentGap = self.calculateGap(car)

        if (currentGap > -1) and (currentGap < smallestGapFound):
            smallestGapFound = currentGap
            successorCar = car

    if successorCar == "":
        return 1 # calling code checks for 1 as an error code
    else:        
        return successorCar

计划是创建汽车对象,然后把它们存储在一个列表里。每次调用findSuccessorMethod时,这个全球的汽车列表会被传递给它,比如:

    c1 = testCar.Car(4, 5, 1) # position, pos_y, Vel, ID
        c2 = testCar.Car(7, 9, 2)
        c3 = testCar.Car(9, 1, 2)
        cars = [c1, c2, c3]

        c1_succ = c1.findSuccessorCar(cars)

这个方法运行得很好:find successor car函数会说汽车c2在汽车c1的前面(位置7在位置4的前面)。

但是,我希望汽车c1能够找出它的直接后继车前面是哪辆车,也就是说,找出在c1前面的车的前面是哪辆车,在这个例子中是汽车c3。我想,如果我执行c1_succ.findSuccessorCars(cars),应该可以正常工作:用type(c1_succ)查看它是一个实例,用hasattr查看它有预期的对象属性。

然而,当我尝试执行c1_succ.findSuccessorCars(cars)时,返回的是一个整数。这让我感到困惑——为什么这样不行?为什么不能以这种方式递归执行类方法?这个整数是从哪里来的?

注意:我感觉这可能和self声明有关,我需要修改我的代码,不仅要有一个全球的汽车列表,还需要有一个它们当前位置信息的全球列表,或者另一个类方法,比如findSuccessorsSuccessor(是的,我知道这个名字很糟糕!)。不过,我想了解为什么这个递归的方法不奏效。

更新

这是计算两辆车之间间隙的代码——我知道这很基础,所以请不要在后面笑得太厉害。

    def calculateGap(self, car):
        ''' Calculate the gap between two cars
        '''
        thisCar = self
        otherCar = car

        gap = otherCar.position_x - thisCar.position_x 

        return gap

2 个回答

2

你的方法理论上是可行的,但这是一个实现上的错误。不过,这并不是正确的做法;具体来说,findSuccessorCar 不应该是 Car 类的方法。这是因为 Car 实例的列表是一个独立的结构;Car 类不应该也不需要知道这个列表的存在。如果你想为这个列表创建一个类,应该创建一个 Road 类,它是一个 Cars 的列表,然后把 findSuccessorCar 放在这个类里。

话虽如此,我不明白为什么你不能这样做

import operator
cars.sort( key = operator.attrgetter( "position" ) )

来按位置顺序对汽车列表进行排序。我觉得你是在实现自己的排序算法来找到下一个汽车吗?

还有几点需要注意:你应该使用异常(raise BadCarMojoError)来表示失败,而不是使用神秘的返回代码;类方法通常使用 cls 作为第一个参数,而不是 self;而且 Car 应该继承自 object


import bisect

class Car( object) :
    def __init__( self, position, id, velocity ):
        self.position = position
        self.id = id
        self.velocity = velocity

    def __lt__( self, other ):
        return self.position < other.position

class Road( object ):
    def __init__( self ):
        self.cars = [ ]

    def driveOn( self, car ):
        bisect.insort( self.cars, car )

    def successor( self, car ):
        i = bisect.bisect_left( self.cars, car )
        if i == len( self.cars ):
            raise ValueError( 'No item found with key at or above: %r' % ( car, ) )
        return self.cars[ i + 1 ]

c1 = Car( 4, 5, 1 )
c2 = Car( 7, 9, 2 )
c3 = Car( 9, 1, 2 )
c1 < c2

road = Road( )

for car in ( c1, c2, c3 ):
    road.driveOn( car )

c1_succ = road.successor( c1 )
5

你所说的类方法其实是实例方法。类方法是作用于整个的,而实例方法是作用于实例的。在这里,我们讨论的是汽车的实例,而不是汽车这个类本身。

class Car(object):
    def __init__(self, position, id, velocity):
        self.position = position
        self.id = id
        self.velocity = velocity

    def __eq__(self, other):
        return self.id == other.id

    def __str__(self):
        return 'Car(%d, %d, %d)' % (self.position, self.id, self.velocity)

    def calculateGap(self, other):
        return other.position - self.position

    def findSuccessor(self, cars):
        ret = smallestGap = None
        for car in cars:
            if car == self:
                continue
            gap = self.calculateGap(car)
            if gap < 0:
                continue
            if smallestGap is None or gap < smallestGap:
                ret, smallestGap = car, gap
        return ret

    def findNthSuccessor(self, n, cars):
        cur = self
        for x in xrange(n):
            cur = cur.findSuccessor(cars)
            if cur is None:
                return None
        return cur

c1 = Car(4, 5, 1)
c2 = Car(7, 9, 2)
c3 = Car(9, 1, 2)
cars = [c1, c2, c3]

print c1.findSuccessor(cars)
print c1.findSuccessor(cars).findSuccessor(cars)
print c1.findNthSuccessor(2, cars)

输出:

Car(7, 9, 2)
Car(9, 1, 2)
Car(9, 1, 2)

撰写回答