1.Object(对象)的理解
对象存在状态,它拥有一组变量/属性和函数。将对象想象成一个盒子,则在盒子里面存放着一些变量来定义这个对象的状态,并且里面通常还会有一些函数,它们则告诉我们这个对象可以做什么!
2.创建一个 Car Object
先介绍一个对象,以对 Object(对象) 有初步的认知。这里以Car对象为例,先介绍如何使用Car对象,最后再介绍Car对象的定义方法。为了创建一个命名为carla的Car对象,必须:
1)Import 我们的car.py,并定义一组car的初始状态变量
2)调用car.Car( ),这个特殊的函数会初始化一个Car对象,同时传入一组初始状态变量
在这个例子中,状态变量由一个位置 position: [y, x] 和 一个速度 velocity: [vy, vx] 定义。最后,我们还要传入一个 world(二维数组)。
3.导入 (Import) 和定义初始变量
# Import statements
import numpy
import car
# Declare initial variables
# Create a 2D world of 0's
height = 4
width = 6
world = np.zeros((height, width))
# Define the initial car state
initial_position = [0, 0] # [y, x] (top-left corner)
velocity = [0, 1] # [vy, vx] (moving to the right)
4.创建并可视化一个Car 对象# Create a car object with these initial params
carla = car.Car(initial_position, velocity, world)
# Display the world
carla.display_world()
输出:
5.命令Car 对象移动
创建的Car对象carla可以通过调用 move() 函数及 turn_left() 函数,以状态变量 velocity 的方向为参考进行移动和左转。carla 的一个行为序列如下(这里的world是6*8的二维数组):

6.学习使用Car Object1) 导入必要的库
import numpy as np
import car
%matplotlib inline
2) 定义变量初始值# Create a 2D world of 0's
height = 4
width = 6
world = np.zeros((height, width))
# Define the initial car state
initial_position = [0, 0] # [y, x] (top-left corner)
velocity = [0, 1] # [vy, vx] (moving to the right)
3) 创建一个Car Object# Create a car object with these initial params
carla = car.Car(initial_position, velocity, world)
print('Carla\'s initial state is: ' + str(carla.state))
输出:Carla's initial state is: [[0, 0], [0, 1]]
4) Move 和追踪状态# Move in the direction of the initial velocity
carla.move()
# Track the change in state
print('Carla\'s state is: ' + str(carla.state))
# Display the world
carla.display_world()
输出:Carla's state is: [[0, 1], [0, 1]]
5) 使用 move() 和 turn_left() 实现以正方形路径移动,下面是我的实现:
## TODO: Make carla traverse a 4x4 square path
## Display the result
# move to start position
carla.turn_left()
carla.turn_left()
carla.move()
# path
for i in range(4):
carla.turn_left()
for i in range(3):
carla.move()
print('Carla\'s state is: ' + str(carla.state))
carla.display_world()
输出:Carla's state is: [[0, 0], [0, -1]]
7.重要内容:Car Class (Car 类)
1) 在上面使用Car Object时,有没有思考过,为什么Python知道Car Object呢?
秘密在于我们之前没有细谈的car.py。在这个python文件里头,定义了一个Class(即类),这个类的名称为Car。这个类允许我们书写car.Car()这样的代码,它会自动调用定义在类里面的特殊函数__init__,如下面所示,car.py 文件的前几行代码。
2) 关于__init__
__init__表示初始化,这个函数释放内存并允许我们创建一个特定的Car对象, carla。然后,Carla可以访问Car类中的所有函数,比如move()和turn_left(),而初始化意味着计算机现在有专门的内存空间来记住这个Car 对象可以做什么,以及它在任何给定的时刻处于什么状态。
8.car.py 文件的一些解释
0) 为什么类的声明时要加(object)?其实也可以不加。声明中加 (ClassX) 代表继承自Class类,在这里表示Car类继承自object类。继承object对象,就拥有了额外的可操作对象,这些都是类中的高级特性。这些高级特性对于那些要着手写框架或者写大型项目的高手来说就比较有用了。在python 3中即使没有加上(object)也会默认帮你加上。
Animal类继承了object对象,拥有了好多可操作对象,这些都是类中的高级特性。
1) class Car(object): 这看起来有点类似函数声明,但是关键字class 让Python 知道接下来的代码应该要描述一个Car 对象的状态和功能, 也即Car 对象的Variables 和 functions.
Animal类继承了object对象,拥有了好多可操作对象,这些都是类中的高级特性。
1) class Car(object): 这看起来有点类似函数声明,但是关键字class 让Python 知道接下来的代码应该要描述一个Car 对象的状态和功能, 也即Car 对象的Variables 和 functions.
2) 对象总是像Car这样首字母大写的.
3) __init__函数负责在内存空间中创建一个特定的Car 对象,如carla.它是变量设置初始值的地方,如 self.state = [position, velocity]
4) move()函数使用一个恒定速度模型将汽车移动到速度、vx和vy的方向,并更新对象的状态。
5) turn_left()函数将速度值旋转到左90度,并更新对象的状态!
6) car.py 文件的神秘面纱!
import matplotlib.pyplot as plt
""" The Car class defines a car's movement and keeps track of its state.
The class includes init, move, and display functions.
This class assumes a constant velocity motion model and the state
of the car includes the car's position, and it's velocity.
Attributes:
state: A list of the car's current position [y, x] and velocity [vy, vx]
world: The world that the car is moving within (a 2D list)
"""
class Car(object):
# Car constructor
# Called when you write car.Car(_, _, _)
def __init__(self, position, velocity, world):
"""Initializes Car with some position, velocity, and a world to traverse."""
# Initialize the state
# Position is a list [y, x] and so is velocity [vy, vx]
self.state = [position, velocity]
self.world = world # world is a 2D list of values that range from 0-1
# Set the default color
self.color = 'r'
# Initalize the path
self.path = []
self.path.append(position)
# Move function
def move(self, dt=1):
""" The move function moves the car in the direction of the velocity and
updates the state.
It assumes a circular world and a default dt = 1 (though dt can be any
non-negative integer).
"""
height = len(self.world)
width = len(self.world[0])
position = self.state[0]
velocity = self.state[1]
# Predict the new position [y, x] based on velocity [vx, vy] and time, dt
predicted_position = [
(position[0] + velocity[0]*dt) % height, # default dt = 1
(position[1] + velocity[1]*dt) % width
]
# Update the state
self.state = [predicted_position, velocity]
# Every time the robot moves, add the new position to the path
self.path.append(predicted_position)
# Turn left function
def turn_left(self):
""" Turning left "rotates" the velocity values, so vy = -vx, and vx = vy.
For example, if a car is going right at 1 world cell/sec this means
vy = 0, vx = 1,
and if it turns left, then it should be moving upwards on the world grid
at the same speed!
And up is vy = -1 and vx = 0
"""
# Change the velocity
velocity = self.state[1]
predicted_velocity = [
-velocity[1],
velocity[0]
]
# Update the state velocity
self.state[1] = predicted_velocity
# Helper function for displaying the world + robot position
# Assumes the world in a 2D numpy array and position is in the form [y, x]
# path is a list of positions, and it's an optional argument
def display_world(self):
# Store the current position of the car
position = self.state[0]
# Plot grid of values + initial ticks
plt.matshow(self.world, cmap='gray')
# Set minor axes in between the labels
ax=plt.gca()
rows = len(self.world)
cols = len(self.world[0])
ax.set_xticks([x-0.5 for x in range(1,cols)],minor=True )
ax.set_yticks([y-0.5 for y in range(1,rows)],minor=True)
# Plot grid on minor axes in gray (width = 2)
plt.grid(which='minor',ls='-',lw=2, color='gray')
# Create a 'x' character that represents the car
# ha = horizontal alignment, va = verical
ax.text(position[1], position[0], 'x', ha='center', va='center', color=self.color, fontsize=30)
# Draw path if it exists
if(len(self.path) > 1):
# loop through all path indices and draw a dot (unless it's at the car's location)
for pos in self.path:
if(pos != position):
ax.text(pos[1], pos[0], '.', ha='center', va='baseline', color=self.color, fontsize=30)
# Display final result
plt.show()
9.修改car.py文件当学会使用Car 类对象,并且看过car.py的代码以后,接下来就是自己向car.py 中添加代码,以使Car类可以实现更多的功能(functions),或是具有更多的状态属性(Variables)。
首先来看向Car类中增加函数: 在上面的car.py 文件中,使Car 对象转向的函数只有 turn_left,即左转.如果我们希望Car 对象还能够向右转,应该怎么修改car.py 文件呢? 我们可以在定义turn_left() 函数的语句下面插入一段模仿 turn_left() 而写的 函数tun_right(),具体如下(摘出修改后的car.py 文件的一段):
# Turn left function
def turn_left(self):
""" Turning left "rotates" the velocity values, so vy = -vx, and vx = vy.
For example, if a car is going right at 1 world cell/sec this means
vy = 0, vx = 1,
and if it turns left, then it should be moving upwards on the world grid
at the same speed!
And up is vy = -1 and vx = 0
"""
# Change the velocity
velocity = self.state[1]
predicted_velocity = [
-velocity[1],
velocity[0]
]
# Update the state velocity
self.state[1] = predicted_velocity
## TODO: Write the turn_right function
## Hint: Use turn_left for inspiration!
def turn_right(self):
velocity = self.state[1]
predicted_velocity = [ velocity[1], -velocity[0] ]
self.state[1] = predicted_velocity
保存car.py 文件的修改之后,我们就能使用turn_right() 函数了,由于是模仿turn_left书写的,因而调用格式和 turn_left 完全一致。10.增加Color
我们知道,在上面的car.py 文件中,__init__是这样定义的:
def __init__(self, position, velocity, world):
"""Initializes Car with some position, velocity, and a world to traverse."""
# Initialize the state
# Position is a list [y, x] and so is velocity [vy, vx]
self.state = [position, velocity]
self.world = world # world is a 2D list of values that range from 0-1
# Set the default color
self.color = 'r'
# Initalize the path
self.path = []
self.path.append(position)
1) 在前面我们提到过__init__是初始化变量的地方。所以在car.py 文件里的__init__() 底下,存在 state, world, color, path这些变量的赋值操作,这就是初始化。这里面有些变量的初始化使用创建对象时传入的参数,有些变量的初始化则使用默认值。那怎么看出来呢?
- 使用传入参数初始化: 这类Variable在__init__下赋值时,使用的值为 def __init__(self, position, velocity, world) 括号内传入的参数。如self.state = [position, velocity]
- 使用默认值初始化: 这类Variable在__init__下直接赋初始值,如self.color = 'r'
2) 将"使用默认值初始化"的变量转变为"使用传入参数初始化"的变量.
这时候,需要将赋值语句做修改,不给self.color 确定的初始值.如由原来的self.color = 'r' 修改为 self.color = color。第二步,在def __init__() 语句的括号内添加相应的需要传入的初始值color。修改后的 __init__() 函数如下:
def __init__(self, position, velocity, world, color):
"""Initializes Car with some position, velocity, and a world to traverse."""
# Initialize the state
# Position is a list [y, x] and so is velocity [vy, vx]
self.state = [position, velocity]
self.world = world # world is a 2D list of values that range from 0-1
# Set the color
self.color = color
# Initalize the path
self.path = []
self.path.append(position)
3) 特别需要说明的是,使用传入参数初始化和使用默认值初始化是可以共存的!如这样修改 def __init__ 语句: def __init__(self, position, velocity, world, color='r'):
这时候,若创建Car对象的时候,没有传入color参数,则创建Car对象的时候会使用默认的color值'r';若传入color参数,则创建对象的时候会使用传入的值。这个内容在”11.实例化多个Car对象“中会体现有所体现,只不过这个时候的变量是新加入的maximum_speed。11.实例化多个Car 对象
实例化多个Car对象的做法没有什么特别之处,也没有涉及新的内容,只要重复调用car.Car() 创建对象就可以了。我们在Car对象中增加了一个新的变量 maximum_speed,最后的car.py 文件如下所示:
import matplotlib.pyplot as plt
""" The Car class defines a car's movement and keeps track of its state.
The class includes init, move, and display functions.
This class assumes a constant velocity motion model and the state
of the car includes the car's position, and it's velocity.
Attributes:
state: A list of the car's current position [y, x] and velocity [vy, vx]
world: The world that the car is moving within (a 2D list)
"""
class Car(object):
# Car constructor
# Called when you write car.Car(_, _, _)
def __init__(self, position, velocity, world, color = 'r', maximum_speed = 160):
"""Initializes Car with some position, velocity, and a world to traverse."""
# Initialize the state
# Position is a list [y, x] and so is velocity [vy, vx]
self.state = [position, velocity]
self.world = world # world is a 2D list of values that range from 0-1
# Set the color
self.color = color
# Set the maximum_speed
self.maximum_speed = maximum_speed
# Initalize the path
self.path = []
self.path.append(position)
# Move function
def move(self, dt=1):
""" The move function moves the car in the direction of the velocity and
updates the state.
It assumes a circular world and a default dt = 1 (though dt can be any
non-negative integer).
"""
height = len(self.world)
width = len(self.world[0])
position = self.state[0]
velocity = self.state[1]
# Predict the new position [y, x] based on velocity [vx, vy] and time, dt
predicted_position = [
(position[0] + velocity[0]*dt) % height, # default dt = 1
(position[1] + velocity[1]*dt) % width
]
# Update the state
self.state = [predicted_position, velocity]
# Every time the robot moves, add the new position to the path
self.path.append(predicted_position)
# Turn left function
def turn_left(self):
""" Turning left "rotates" the velocity values, so vy = -vx, and vx = vy.
For example, if a car is going right at 1 world cell/sec this means
vy = 0, vx = 1,
and if it turns left, then it should be moving upwards on the world grid
at the same speed!
And up is vy = -1 and vx = 0
"""
# Change the velocity
velocity = self.state[1]
predicted_velocity = [
-velocity[1],
velocity[0]
]
# Update the state velocity
self.state[1] = predicted_velocity
# Helper function for displaying the world + robot position
# Assumes the world in a 2D numpy array and position is in the form [y, x]
# path is a list of positions, and it's an optional argument
def display_world(self):
# Store the current position of the car
position = self.state[0]
# Plot grid of values + initial ticks
plt.matshow(self.world, cmap='gray')
# Set minor axes in between the labels
ax=plt.gca()
rows = len(self.world)
cols = len(self.world[0])
ax.set_xticks([x-0.5 for x in range(1,cols)],minor=True )
ax.set_yticks([y-0.5 for y in range(1,rows)],minor=True)
# Plot grid on minor axes in gray (width = 2)
plt.grid(which='minor',ls='-',lw=2, color='gray')
# Create a 'x' character that represents the car
# ha = horizontal alignment, va = verical
ax.text(position[1], position[0], 'x', ha='center', va='center', color=self.color, fontsize=30)
# Draw path if it exists
if(len(self.path) > 1):
# loop through all path indices and draw a dot (unless it's at the car's location)
for pos in self.path:
if(pos != position):
ax.text(pos[1], pos[0], '.', ha='center', va='baseline', color=self.color, fontsize=30)
# Display final result
plt.show()
创建多个Car 对象的代码如下: import numpy as np
import car
%matplotlib inline
# Auto-reload function so that this notebook keeps up with
# changes in the class file
%load_ext autoreload
%autoreload 2
定义初始值: # Create a 2D world of 0's
height = 4
width = 6
world = np.zeros((height, width))
# Define the initial car state
initial_position = [0, 0] # [y, x] (top-left corner)
velocity = [0, 1] # [vy, vx] (moving to the right)
创建两辆颜色不同的车,并且可视化它们的world [position1, position2] = [[1,1], [3,3]]
[velocity1, velocity2] = [[0,1], [1,0]]
[color1, color2] = ['g', 'y']
tokyo = car.Car(position1, velocity1, world, color1) # parameter "maximum_speed" use default value 160
tesla = car.Car(position2, velocity2, world, color2, 120) # parameter "maximum_speed" use value 120
tokyo.move()
tesla.move()
tokyo.display_world()
tesla.display_world()
print("tokyo's maximum speed is " + str(tokyo.maximum_speed))
print("tesla's maximum speed is " + str(tesla.maximum_speed))
输出: tokyo's maximum speed is 160
tesla's maximum speed is 120
12.Color 类import matplotlib.pyplot as plt
'''
The color class creates a color from 3 values, r, g, and b (red, green, and blue).
attributes:
r - a value between 0-255 for red
g - a value between 0-255 for green
b - a value between 0-255 for blue
'''
class Color(object):
# __init__ is called when a color is constructed using color.Color(_, _, _)
def __init__(self, r, g, b):
# Setting the r value
self.r = r
## TODO: Set the other two color variables g and b
self.g = g
self.b = b
# __repr__ is called when a color is printed using print(some_color)
# It must return a string
def __repr__(self):
'''Display a color swatch and then return a text description of r,g,b values.'''
plt.imshow([[(self.r/255, self.g/255, self.b/255)]])
## TODO: Write a string representation for the color
## ex. "rgb = [self.r, self.g, self.b]"
## Right now this returns an empty string
string = 'rgb = '+ str([self.r, self.g, self.b])
return string
创建一个color 对象: # Notice we are importing the color class!
import numpy as np
import color
%matplotlib inline
%load_ext autoreload
%autoreload 2
输出: The autoreload extension is already loaded. To reload it, use:
%reload_ext autoreload
定义color并将其打印: # r, g, b
r = 200
g = 0
b = 200
# Create the color object
test_color = color.Color(r, g, b)
# This will throw an error if the class code is incomplete
print(test_color) # automatically call __repr__
输出: rgb = [[200, 0, 200]]
13.关于重载函数(Overloading Functions), 重载Color Addtion
1) 关于双下划线 __X__
我们已经见过一些有两个下划线的函数例子,比如:
__init__ 和 __repr__
这些是Python以特定方式使用的特殊函数(special functions)。我们通常不会像我们使用move() 和“turn_left()”那样,直接调用这些函数。相反地,Python会根据我们对关键字和操作符的使用自动调用它们。例如,当我们创建一个新对象时,__init__()会被调用,当我们告诉Python打印一个特定对象的字符串表示时,会调用__repr__()。
2) 另一个例子: __add__
所有这些 special functions 都是在双下划线之间写的,而且有很多这样的函数!要查看这些函数的完整列表,请查看Python文档。例如,我们可以通过定义 __add__ 函数来决定将两个Car对象使用+符号加在一起后会发生什么。
def __add__(self, other):
# Create an empty list
added_state = []
# Add the states together, element-wise
for i in range(self.state):
added_value = self.state[i] + other.state[i]
added_state.append(added_value)
return added_state
上面的版本,添加了状态变量!或者你也可以选择只打印添加车辆是无效操作,如下面代码所示。 def __add__(self, other):
# Print an error message and return the unchanged, first state
print('Adding two cars is an invalid operation!')
return self.state
3) 操作符重载(Operator Overloading)
我们在类里面定义这些函数的行为,就叫操作符重载. 在这种情况下,重载只是意味着:给一个标准的操作符( 比如加法 + )提供一个以上的含义。操作符重载是一个强大的工具,它不仅在类中反复出现,而且它对编写直观且易于使用的类很有帮助。所以,当你继续学习的时候,一定要记住这一点!
Color 类的定义( 注意对 __add__ 的操作符重载 ):
import matplotlib.pyplot as plt
'''
The color class creates a color from 3 values, r, g, and b (red, green, and blue).
attributes:
r - a value between 0-255 for red
g - a value between 0-255 for green
b - a value between 0-255 for blue
'''
class Color(object):
# Initializes a color with rgb values
def __init__(self, r, g, b):
self.r = r
self.g = g
self.b = b
# Called when a Color object is printed out
def __repr__(self):
'''Display a color swatch and returns a text description of r,g,b values'''
plt.imshow([[(self.r/255, self.g/255, self.b/255)]])
return 'r, g, b = ' + str(self.r) + ', ' + str(self.g) + ', ' + str(self.b)
## TODO: Complete this add function to add two colors together
def __add__(self, other):
'''Adds the r, g, and b components of each color together
and averaging them.
The new Color object, with these averaged rgb values,
is returned.'''
return Color((self.r+other.r)/2, (self.g+other.g)/2, (self.b+other.b)/2)
导入color: # Notice we are importing the color class!
import numpy as np
import color
%matplotlib inline
# Auto-reload function so that this notebook keeps up with
# changes in the class file
%load_ext autoreload
%autoreload 2
定义多个颜色并打印: color1 = color.Color(250, 0, 0)
print(color1)
输出: color2 = color.Color(0, 50, 200)
print(color2)
输出: 对上面两种颜色执行加操作:
# Add the two colors to create a *new* color object
new_color = color1 + color2
print(new_color)
输出: