python - Python Class - 为什么在设置实例变量时必须将我的类变量设置为等于自身
问题描述
我正在学习 Udacity 的机器人人工智能课程,遇到了一些令人费解的事情。该课程编写了一个机器人类供我们使用(我不相信这段代码)。
有一种set
方法用于设置机器人的 x、y 和方向,它们是类中的参数。机器人(30,50,pi/2)
在 2D 世界中被初始化,并且在 2 次移动之后(-pi/2, 15)
,(-pi/2, 10)
感知函数应该估计 ~[32.0156, 53.1507, 47.1699, 40.3112]
作为到地标的距离。在底部的驱动程序代码中,我发现我的答案会有所不同,具体取决于我在.set()
方法之后是否将类实例设置为等于自身。我不明白为什么这很重要。我认为该.set()
方法会更新该类变量的实例。没有等号,我对[31.6227, 58.3095, 31.6227, 58.3095]
.
我正在谈论的驱动程序代码是:
myrobot = robot()
myrobot.set(30, 50, pi/2)
myrobot = myrobot.move(-(pi/2), 15)
print(myrobot.sense())
myrobot = myrobot.move(-(pi/2), 10)
print(myrobot.sense())
对...
myrobot = robot()
myrobot.set(30, 50, pi/2)
myrobot.move(-(pi/2), 15)
print(myrobot.sense())
myrobot.move(-(pi/2), 10)
print(myrobot.sense())
机器人类:
# Make a robot called myrobot that starts at
# coordinates 30, 50 heading north (pi/2).
# Have your robot turn clockwise by pi/2, move
# 15 m, and sense. Then have it turn clockwise
# by pi/2 again, move 10 m, and sense again.
#
# Your program should print out the result of
# your two sense measurements.
#
# Don't modify the code below. Please enter
# your code at the bottom.
from math import *
import random
landmarks = [[20.0, 20.0], [80.0, 80.0], [20.0, 80.0], [80.0, 20.0]]
world_size = 100.0
class robot:
def __init__(self):
self.x = random.random() * world_size
self.y = random.random() * world_size
self.orientation = random.random() * 2.0 * pi
self.forward_noise = 0.0;
self.turn_noise = 0.0;
self.sense_noise = 0.0;
def set(self, new_x, new_y, new_orientation):
if new_x < 0 or new_x >= world_size:
raise (ValueError, 'X coordinate out of bound')
if new_y < 0 or new_y >= world_size:
raise (ValueError, 'Y coordinate out of bound')
if new_orientation < 0 or new_orientation >= 2 * pi:
raise (ValueError, 'Orientation must be in [0..2pi]')
self.x = float(new_x)
self.y = float(new_y)
self.orientation = float(new_orientation)
def set_noise(self, new_f_noise, new_t_noise, new_s_noise):
# makes it possible to change the noise parameters
# this is often useful in particle filters
self.forward_noise = float(new_f_noise);
self.turn_noise = float(new_t_noise);
self.sense_noise = float(new_s_noise);
def sense(self):
Z = []
for i in range(len(landmarks)):
dist = sqrt((self.x - landmarks[i][0]) ** 2 + (self.y - landmarks[i][1]) ** 2)
dist += random.gauss(0.0, self.sense_noise)
Z.append(dist)
return Z
def move(self, turn, forward):
if forward < 0:
raise (ValueError, 'Robot cant move backwards')
# turn, and add randomness to the turning command
orientation = self.orientation + float(turn) + random.gauss(0.0, self.turn_noise)
orientation %= 2 * pi
# move, and add randomness to the motion command
dist = float(forward) + random.gauss(0.0, self.forward_noise)
x = self.x + (cos(orientation) * dist)
y = self.y + (sin(orientation) * dist)
x %= world_size # cyclic truncate
y %= world_size
# set particle
res = robot()
res.set(x, y, orientation)
res.set_noise(self.forward_noise, self.turn_noise, self.sense_noise)
return res
def Gaussian(self, mu, sigma, x):
# calculates the probability of x for 1-dim Gaussian with mean mu and var. sigma
return exp(- ((mu - x) ** 2) / (sigma ** 2) / 2.0) / sqrt(2.0 * pi * (sigma ** 2))
def measurement_prob(self, measurement):
# calculates how likely a measurement should be
prob = 1.0;
for i in range(len(landmarks)):
dist = sqrt((self.x - landmarks[i][0]) ** 2 + (self.y - landmarks[i][1]) ** 2)
prob *= self.Gaussian(dist, self.sense_noise, measurement[i])
return prob
def __repr__(self):
return '[x=%.6s y=%.6s orient=%.6s]' % (str(self.x), str(self.y), str(self.orientation))
def eval(r, p):
sum = 0.0;
for i in range(len(p)): # calculate mean error
dx = (p[i].x - r.x + (world_size/2.0)) % world_size - (world_size/2.0)
dy = (p[i].y - r.y + (world_size/2.0)) % world_size - (world_size/2.0)
err = sqrt(dx * dx + dy * dy)
sum += err
return sum / float(len(p))
#### DON'T MODIFY ANYTHING ABOVE HERE! ENTER CODE BELOW ####
myrobot = robot()
myrobot.set(30, 50, pi/2)
myrobot = myrobot.move(-(pi/2), 15)
print(myrobot.sense())
myrobot = myrobot.move(-(pi/2), 10)
print(myrobot.sense())
解决方案
1.) myrobot = robot()
myrobot.set(30, 50, pi/2)
myrobot = myrobot.move(-(pi/2), 15)
print(myrobot.sense())
myrobot = myrobot.move(-(pi/2), 10)
print(myrobot.sense())
在上面的代码中,您按照所提出的问题做的是正确的,即将机器人设置为 (30,50, pi/2) 并在一次移动后感知并在第二次移动后再次感知。
由于move方法正在返回一个新实例,该实例现在已从其原始位置移动(即在 [30,50,pi/2] 处),或者您可以说在move方法中 myrobot 对象的位置未设置但程序员正在设置位置新实例(即 res)。因此,您声明的 myrobot 的位置将保持不变,因此为避免这种情况,您需要使用move方法返回的对象重新实例化。
2.) myrobot = robot()
myrobot.set(30, 50, pi/2)
myrobot.move(-(pi/2), 15)
print(myrobot.sense())
myrobot.move(-(pi/2), 10)
print(myrobot.sense())
但是,在 2.) 代码中,即使移动机器人两次,您也会得到相同的感知结果。2.) 代码中上述两条打印指令的结果将与您将机器人设置为 (30,50, pi/2) 时相同,并且保持不变,因为在move方法中,正在创建新实例并定位正在更改此新实例。
由于您要更改的属性不是类属性(即 x、y 和方向并非对每个对象都通用)。这就是为什么通过改变一个对象的属性不会改变另一个对象的相应属性的原因。因为类的每个实例都有自己的属性和方法副本。
推荐阅读
- php - 是否可以使用 openssl 将文件解密到内存而不是磁盘文件
- google-chrome-extension - 在 chrome 扩展中获取和设置本地存储
- jmeter - JMeter中生成的PerfMon png中y轴的单位是什么?
- typescript - 通用、联合和扩展。打字稿
- reactjs - 如何防止用户输入以数字开头的用户名,但只允许在字母字符后输入数字?
- python - Python:如何在循环中只打印一行
- android - 使用 shrinkResources true 时未找到可绘制对象
- cert-manager - 是否可以使用 cert-manager 颁发手动创建的证书?
- javascript - 如何持续关注 MongoDB 并更新我的 UI?
- python - Python在引发异常后添加重试