参考资料:
- AILearning
- 菜鸟教程
使用Jupyter进行练习
python-version: 3.9
面向对象思想请看: [ XJTUSE ]JAVA语言基础知识——第一章 面向对象程序设计思想
属性是与对象绑定的一组数据,可以只读,只写,或者读写,使用时不加括号,例如:
f = open("new_file", 'w')
# 显示模式属性
print("模式属性:", f.mode)
print("是否关闭属性:", f.closed)
模式属性: w
是否关闭属性: False
方法是与属性绑定的一组函数,需要使用括号,作用于对象本身:
f.write('Hi.\n')
f.seek(0)
f.write('Hola!\n')
f.close()
import matplotlib.pyplot as plt
import numpy as np
class Forest(object):
def __init__(self, size=(150, 150), p_sapling=0.0025, p_lightning=5.e-6, name=None):
self.size = size
self.trees = np.zeros(self.size, dtype=bool)
self.forest_fires = np.zeros(self.size, dtype=bool)
self.p_sapling = p_sapling
self.p_lightning = p_lightning
if name is not None:
self.name = name
else:
self.name = self.__class__.__name__
@property
def num_cells(self):
return self.size[0] * self.size[1]
@property
def tree_fraction(self):
return self.trees.sum() / float(self.num_cells)
@property
def fire_fraction(self):
return self.forest_fires.sum() / float(self.num_cells)
def advance_one_step(self):
self.grow_trees()
self.start_fires()
self.burn_trees()
def grow_trees(self):
growth_sites = self._rand_bool(self.p_sapling)
self.trees[growth_sites] = True
def start_fires(self):
lightning_strikes = (self._rand_bool(self.p_lightning) &
self.trees)
self.forest_fires[lightning_strikes] = True
def burn_trees(self):
fires = np.zeros((self.size[0] + 2, self.size[1] + 2), dtype=bool)
fires[1:-1, 1:-1] = self.forest_fires
north = fires[:-2, 1:-1]
south = fires[2:, 1:-1]
east = fires[1:-1, :-2]
west = fires[1:-1, 2:]
new_fires = (north | south | east | west) & self.trees
self.trees[self.forest_fires] = False
self.forest_fires = new_fires
def _rand_bool(self, p):
return np.random.uniform(size=self.trees.shape) < p
创建一个新的森林类对象:
forest = Forest()
print("树的状态:\n",forest.trees)
print("燃烧的状态: \n",forest.forest_fires)
树的状态:
[[False False False ... False False False]
[False False False ... False False False]
[False False False ... False False False]
...
[False False False ... False False False]
[False False False ... False False False]
[False False False ... False False False]]
燃烧的状态:
[[False False False ... False False False]
[False False False ... False False False]
[False False False ... False False False]
...
[False False False ... False False False]
[False False False ... False False False]
[False False False ... False False False]]
# 使用 matshow 进行可视化:
plt.matshow(forest.trees, cmap=plt.cm.Greens)
plt.show()
# 经过一段时间
forest.advance_one_step()
plt.matshow(forest.trees, cmap=plt.cm.Greens)
plt.show()
# 循环很长时间:
for i in range(500):
forest.advance_one_step()
plt.matshow(forest.trees, cmap=plt.cm.Greens)
print(forest.tree_fraction)
0.21697777777777777
# 迭代更长时间
forest = Forest()
tree_fractions = []
for i in range(5000):
forest.advance_one_step()
tree_fractions.append(forest.tree_fraction)
fig = plt.figure()
ax0 = fig.add_subplot(1,2,1)
ax0.matshow(forest.trees, cmap=plt.cm.Greens)
ax1 = fig.add_subplot(1,2,2)
ax1.plot(tree_fractions)
plt.show()
在 Python 中,几乎所有的东西都是对象。
整数是对象:
a = 257
print(type(a))
print("a内存标识:",id(a))
b = a
print("b和a是同一个对象?",b is a)
a内存标识: 2352921776272
b和a是同一个对象? True
函数是对象:
def foo():
print("hi")
print(type(foo))
print(id(foo))
# type 函数本身也是对象:
print(type(type))
print(id(type))
2352922378096
140727138868320
只有一些保留的关键词不是对象:
id(if)
Cell In [21], line 1
id(if)
^
SyntaxError: invalid syntax
class
定义如下:
class ClassName(ParentClass):
"""class docstring"""
def method(self):
return
class
关键词在最前面ClassName
通常采用 CamelCase
记法ParentClass
用来表示继承关系""""""
中的内容表示 docstring
,可以省略self
参数表示这个对象本身class
中的方法要进行缩进class Forest(object):
""" Forest can grow trees which eventually die."""
pass
其中 object
是最基本的类型。
查看帮助:
np.info(Forest)
Forest()
Forest can grow trees which eventually die.
forest = Forest()
forest
<__main__.Forest at 0x223d5165520>
可以直接添加属性(有更好的替代方式):
forest.trees = np.zeros((150, 150), dtype=bool)
forest.trees
array([[False, False, False, ..., False, False, False],
[False, False, False, ..., False, False, False],
[False, False, False, ..., False, False, False],
...,
[False, False, False, ..., False, False, False],
[False, False, False, ..., False, False, False],
[False, False, False, ..., False, False, False]])
# 再新建一个实例则没有这个属性
forest2 = Forest()
forest2.trees
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In [28], line 3
1 # 再新建一个实例则没有这个属性
2 forest2 = Forest()
----> 3 forest2.trees
AttributeError: 'Forest' object has no attribute 'trees'
添加方法时,默认第一个参数是对象本身,一般为 self
,可能用到也可能用不到,然后才是其他的参数:
class Forest(object):
""" Forest can grow trees which eventually die."""
def grow(self):
print("the tree is growing")
def number(self, num=1):
if num == 1:
print('there is 1 tree.')
else:
print('there are', num, 'trees.')
forest = Forest()
forest.grow()
forest.number(12)
the tree is growing
there are 12 trees.
Python 使用 __
开头的名字来定义特殊的方法和属性,它们有:
__init__()
__repr__()
__str__()
__call__()
__iter__()
__add__()
__sub__()
__mul__()
__rmul__()
__class__
__name__
之前说到,在产生对象之后,我们可以向对象中添加属性。事实上,还可以通过构造方法,在构造对象的时候直接添加属性:
class Leaf(object):
"""
A leaf falling in the woods.
"""
def __init__(self, color='green'):
self.color = color
l1 = Leaf()
print(l1.color)
l2 = Leaf("red")
print(l2.color)
green
red
回到森林的例子:
import numpy as np
class Forest(object):
""" Forest can grow trees which eventually die."""
def __init__(self):
# 构造方法中定义了两个属性 trees 和 fires
self.trees = np.zeros((150,150), dtype=bool)
self.fires = np.zeros((150,150), dtype=bool)
forest = Forest()
print(forest.trees)
print(forest.fires)
[[False False False ... False False False]
[False False False ... False False False]
[False False False ... False False False]
...
[False False False ... False False False]
[False False False ... False False False]
[False False False ... False False False]]
[[False False False ... False False False]
[False False False ... False False False]
[False False False ... False False False]
...
[False False False ... False False False]
[False False False ... False False False]
[False False False ... False False False]]
修改属性的值:
forest.trees[0,0]=True
forest.trees
array([[ True, False, False, ..., False, False, False],
[False, False, False, ..., False, False, False],
[False, False, False, ..., False, False, False],
...,
[False, False, False, ..., False, False, False],
[False, False, False, ..., False, False, False],
[False, False, False, ..., False, False, False]])
改变它的属性值不会影响其他对象的属性值:
forest2 = Forest()
forest2.trees
array([[False, False, False, ..., False, False, False],
[False, False, False, ..., False, False, False],
[False, False, False, ..., False, False, False],
...,
[False, False, False, ..., False, False, False],
[False, False, False, ..., False, False, False],
[False, False, False, ..., False, False, False]])
事实上,__new__()
才是真正产生新对象的方法,__init__()
只是对对象进行了初始化,所以:
leaf = Leaf()
相当于
my_new_leaf = Leaf.__new__(Leaf)
Leaf.__init__(my_new_leaf)
leaf = my_new_leaf
__repr__()
和 __str__()
class Leaf(object):
"""
A leaf falling in the woods.
"""
def __init__(self, color='green'):
self.color = color
def __str__(self):
"This is the string that is printed."
return "A {} leaf".format(self.color)
def __repr__(self):
"This string recreates the object."
return "{}(color='{}')".format(self.__class__.__name__, self.color)
__str__()
是使用 print
函数显示的结果:
leaf = Leaf()
print(leaf)
A green leaf
__repr__()
返回的是不使用 print
方法的结果
leaf
Leaf(color='green')
回到森林的例子:
import numpy as np
class Forest(object):
""" Forest can grow trees which eventually die."""
def __init__(self, size=(150,150)):
self.size = size
self.trees = np.zeros(self.size, dtype=bool)
self.fires = np.zeros((self.size), dtype=bool)
def __repr__(self):
my_repr = "{}(size={})".format(self.__class__.__name__, self.size)
return my_repr
def __str__(self):
return self.__class__.__name__
forest = Forest()
print(forest)
forest
Forest
Forest(size=(150, 150))
__name__
和 __class__
为特殊的属性:
print("forest所属的类:", forest.__class__)
print("forest所属的类的名字:", forest.__class__.__name__)
forest所属的类:
forest所属的类的名字: Forest
只读属性,顾名思义,指的是只可读不可写的属性,之前我们定义的属性都是可读可写的,对于只读属性,我们需要使用 @property
修饰符来得到:
class Leaf(object):
def __init__(self, mass_mg):
self.mass_mg = mass_mg
# 这样 mass_oz 就变成属性了
@property
def mass_oz(self):
return self.mass_mg * 3.53e-5
这里 mass_oz
就是一个只读不写的属性(注意是属性不是方法),而 mass_mg
是可读写的属性:
leaf = Leaf(200)
print ("{0:.5f}".format(leaf.mass_oz))
0.0071
可以修改 mass_mg
属性来改变 mass_oz
:
leaf.mass_mg = 150
print("{0:.5f}".format(leaf.mass_oz))
0.00529
# 是只读属性,不可写:
leaf.mass_oz = 0.001
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In [60], line 2
1 # 是只读属性,不可写:
----> 2 leaf.mass_oz = 0.001
AttributeError: can't set attribute
回到 forest 的例子,我们希望加入几个只读属性:
import numpy as np
class Forest(object):
""" Forest can grow trees which eventually die."""
def __init__(self, size=(150,150)):
self.size = size
self.trees = np.zeros(self.size, dtype=bool)
self.fires = np.zeros((self.size), dtype=bool)
def __repr__(self):
my_repr = "{}(size={})".format(self.__class__.__name__, self.size)
return my_repr
def __str__(self):
return self.__class__.__name__
@property
def num_cells(self):
"""Number of cells available for growing trees"""
return np.prod(self.size)
@property
def tree_fraction(self):
"""
Fraction of trees
"""
num_trees = self.trees.sum()
return float(num_trees) / self.num_cells
@property
def fire_fraction(self):
"""
Fraction of fires
"""
num_fires = self.fires.sum()
return float(num_fires) / self.num_cells
# 查看属性:
forest = Forest()
forest.num_cells
22500
生成一个较小的森林:
small_forest = Forest((10, 10))
small_forest.num_cells
# 初始状态下,树和火灾的比例都是 0:
print(small_forest.tree_fraction)
print(small_forest.fire_fraction)
0.0
0.0
对于 @property
生成的只读属性,我们可以使用相应的 @attr.setter
修饰符来使得这个属性变成可写的:
class Leaf(object):
def __init__(self, mass_mg):
self.mass_mg = mass_mg
# 这样 mass_oz 就变成属性了
@property
def mass_oz(self):
return self.mass_mg * 3.53e-5
# 使用 mass_oz.setter 修饰符
@mass_oz.setter
def mass_oz(self, m_oz):
self.mass_mg = m_oz / 3.53e-5
leaf = Leaf(200)
print ("{0:.5f}".format(leaf.mass_oz))
leaf.mass_mg = 150
print ("{0:.5f}".format(leaf.mass_oz))
# 修改 mass_oz 属性:
leaf.mass_oz = 0.01
print ("{0:.5f}".format(leaf.mass_mg))
0.00706
0.00529
283.28612
# 一个等价的替代如下:
class Leaf(object):
def __init__(self, mass_mg):
self.mass_mg = mass_mg
def get_mass_oz(self):
return self.mass_mg * 3.53e-5
def set_mass_oz(self, m_oz):
self.mass_mg = m_oz / 3.53e-5
mass_oz = property(get_mass_oz, set_mass_oz)
l = Leaf(12)
print(l.get_mass_oz())
l.set_mass_oz(0.02)
print(l.get_mass_oz())
0.0004236
0.02
grow_trees()
方法bool
矩阵,大小与森林大小一致trees
中相应位置设为 True
import numpy as np
class Forest(object):
""" Forest can grow trees which eventually die."""
def __init__(self, size=(150,150), p_sapling=0.0025, p_lightning=5.0e-6):
self.size = size
self.trees = np.zeros(self.size, dtype=bool)
self.fires = np.zeros((self.size), dtype=bool)
# The probability of a tree growing
self.p_sapling = p_sapling
# The probability of lightning striking
self.p_lightning = p_lightning
def __repr__(self):
my_repr = "{}(size={})".format(self.__class__.__name__, self.size)
return my_repr
def __str__(self):
return self.__class__.__name__
@property
def num_cells(self):
"""Number of cells available for growing trees"""
return np.prod(self.size)
@property
def tree_fraction(self):
"""
Fraction of trees
"""
num_trees = self.trees.sum()
return float(num_trees) / self.num_cells
@property
def fire_fraction(self):
"""
Fraction of fires
"""
num_fires = self.fires.sum()
return float(num_fires) / self.num_cells
def _rand_bool(self, p):
"""
Random boolean distributed according to p, less than p will be True
"""
return np.random.uniform(size=self.trees.shape) < p
def grow_trees(self):
"""
Growing trees.
"""
growth_sites = self._rand_bool(self.p_sapling)
self.trees[growth_sites] = True
forest = Forest()
print (forest.tree_fraction)
forest.grow_trees()
print (forest.tree_fraction)
0.0
0.0026666666666666666
1、定义 start_fires()
:
按照给定的概率生成被闪电击中的位置
如果闪电击中的位置有树,那么将其设为着火点
2、定义 burn_trees()
:
3、定义 advance_one_step()
:
import numpy as np
class Forest(object):
""" Forest can grow trees which eventually die."""
def __init__(self, size=(150,150), p_sapling=0.0025, p_lightning=5.0e-6):
self.size = size
self.trees = np.zeros(self.size, dtype=bool)
self.fires = np.zeros((self.size), dtype=bool)
# The probability of a tree growing
self.p_sapling = p_sapling
# The probability of lightning striking
self.p_lightning = p_lightning
def __repr__(self):
my_repr = "{}(size={})".format(self.__class__.__name__, self.size)
return my_repr
def __str__(self):
return self.__class__.__name__
@property
def num_cells(self):
"""Number of cells available for growing trees"""
return np.prod(self.size)
@property
def tree_fraction(self):
"""
Fraction of trees
"""
num_trees = self.trees.sum()
return float(num_trees) / self.num_cells
@property
def fire_fraction(self):
"""
Fraction of fires
"""
num_fires = self.fires.sum()
return float(num_fires) / self.num_cells
def _rand_bool(self, p):
"""
Random boolean distributed according to p, less than p will be True
"""
return np.random.uniform(size=self.trees.shape) < p
def grow_trees(self):
"""
Growing trees.
"""
growth_sites = self._rand_bool(self.p_sapling)
self.trees[growth_sites] = True
def start_fires(self):
"""
Start of fire.
"""
lightning_strikes = (self._rand_bool(self.p_lightning) &
self.trees)
self.fires[lightning_strikes] = True
def burn_trees(self):
"""
Burn trees.
"""
fires = np.zeros((self.size[0] + 2, self.size[1] + 2), dtype=bool)
fires[1:-1, 1:-1] = self.fires
north = fires[:-2, 1:-1]
south = fires[2:, 1:-1]
east = fires[1:-1, :-2]
west = fires[1:-1, 2:]
new_fires = (north | south | east | west) & self.trees
self.trees[self.fires] = False
self.fires = new_fires
def advance_one_step(self):
"""
Advance one step
"""
self.grow_trees()
self.start_fires()
self.burn_trees()
import matplotlib.pyplot as plt
from matplotlib import cm
forest = Forest()
for i in range(100):
forest.advance_one_step()
# 使用 matshow() 显示树木图像:
plt.matshow(forest.trees, cmap=cm.Greens)
plt.show()
# 查看不同着火概率下的森林覆盖率趋势变化:
forest1 = Forest()
forest2 = Forest(p_lightning=5e-4)
tree_fraction1 = []
tree_fraction2 = []
for i in range(2500):
forest1.advance_one_step()
forest2.advance_one_step()
tree_fraction1.append(forest1.tree_fraction)
tree_fraction2.append(forest2.tree_fraction)
plt.plot(tree_fraction1,"r-",label = r"$forest1 = 5\times 10^{-6}$")
plt.plot(tree_fraction2,"b-",label = r"$forest2 = 5\times 10^{-4}$")
plt.xlabel('Number of iterations')
plt.ylabel('Forest coverage rate')
plt.legend()
plt.show()
一个类定义的基本形式如下:
class ClassName(ParentClass):
"""class docstring"""
def method(self):
return
class
关键词在最前面ClassName
通常采用 CamelCase
记法ParentClass
用来表示继承关系""""""
中的内容表示 docstring
,可以省略self
参数表示这个对象本身class
中的方法要进行缩进在里面有一个 ParentClass
项,用来进行继承,被继承的类是父类,定义的这个类是子类。 对于子类来说,继承意味着它可以使用所有父类的方法和属性,同时还可以定义自己特殊的方法和属性。
假设我们有这样一个父类:
class Leaf(object):
def __init__(self, color="green"):
self.color = color
def fall(self):
print ("Splat!")
leaf = Leaf()
print(leaf.color)
leaf.fall()
green
Splat!
现在定义一个子类,继承自 Leaf
:
class MapleLeaf(Leaf):
def change_color(self):
if self.color == "green":
self.color = "red"
继承父类的所有方法:
mleaf = MapleLeaf()
print (mleaf.color)
mleaf.fall()
# 但是有自己独有的方法,父类中没有:
mleaf.change_color()
print (mleaf.color)
green
Splat!
red
如果想对父类的方法进行修改,只需要在子类中重定义这个方法即可
class MapleLeaf(Leaf):
def change_color(self):
if self.color == "green":
self.color = "red"
def fall(self):
self.change_color()
print("Plunk!")
mleaf = MapleLeaf()
print (mleaf.color)
mleaf.fall()
print (mleaf.color)
green
Plunk!
red
多重继承,指的是一个类别可以同时从多于一个父类继承行为与特征的功能,Python
是支持多重继承的:
class Leaf(object):
def __init__(self, color='green'):
self.color = color
class ColorChangingLeaf(Leaf):
def change(self, new_color='brown'):
self.color = new_color
class DeciduousLeaf(Leaf):
def fall(self):
print ("Plunk!")
class MapleLeaf(ColorChangingLeaf, DeciduousLeaf):
pass
在上面的例子中, MapleLeaf
就使用了多重继承,它可以使用两个父类的方法:
leaf = MapleLeaf()
leaf.change("yellow")
print (leaf.color)
leaf.fall()
yellow
Plunk!
如果同时实现了不同的接口,那么,最后使用的方法以继承的顺序为准,放在前面的优先继承:
class Leaf(object):
def __init__(self, color='green'):
self.color = color
class ColorChangingLeaf(Leaf):
def change(self, new_color='brown'):
self.color = new_color
def fall(self):
print ("Spalt!")
class DeciduousLeaf(Leaf):
def fall(self):
print ("Plunk!")
class MapleLeaf(ColorChangingLeaf, DeciduousLeaf):
pass
leaf = MapleLeaf()
leaf.fall()
Spalt!
class MapleLeaf(DeciduousLeaf, ColorChangingLeaf):
pass
leaf = MapleLeaf()
leaf.fall()
Plunk!
事实上,这个顺序可以通过该类的 __mro__
属性或者 mro()
方法来查看:
MapleLeaf.__mro__
(__main__.MapleLeaf,
__main__.DeciduousLeaf,
__main__.ColorChangingLeaf,
__main__.Leaf,
object)
考虑更复杂的例子:
class A(object):
pass
class B(A):
pass
class C(A):
pass
class C1(C):
pass
class B1(B):
pass
class D(B1, C):
pass
D.mro()
[__main__.D, __main__.B1, __main__.B, __main__.C, __main__.A, object]
super(CurrentClassName, instance)
返回该类实例对应的父类对象。
class Leaf(object):
def __init__(self, color="green"):
self.color = color
def fall(self):
print ("Splat!")
class MapleLeaf(Leaf):
def change_color(self):
if self.color == "green":
self.color = "red"
def fall(self):
self.change_color()
super(MapleLeaf, self).fall()
这里,我们先改变树叶的颜色,然后再找到这个实例对应的父类,并调用父类的 fall()
方法:
mleaf = MapleLeaf()
print (mleaf.color)
mleaf.fall()
print (mleaf.color)
green
Splat!
red
回到我们的森林例子,这里我们将森林 Forest
作为父类,并定义一个子类 BurnableForest
:
import numpy as np
class Forest(object):
""" Forest can grow trees which eventually die."""
def __init__(self, size=(150,150), p_sapling=0.0025):
self.size = size
self.trees = np.zeros(self.size, dtype=bool)
self.p_sapling = p_sapling
def __repr__(self):
my_repr = "{}(size={})".format(self.__class__.__name__, self.size)
return my_repr
def __str__(self):
return self.__class__.__name__
@property
def num_cells(self):
"""Number of cells available for growing trees"""
return np.prod(self.size)
@property
def tree_fraction(self):
"""
Fraction of trees
"""
num_trees = self.trees.sum()
return float(num_trees) / self.num_cells
def _rand_bool(self, p):
"""
Random boolean distributed according to p, less than p will be True
"""
return np.random.uniform(size=self.trees.shape) < p
def grow_trees(self):
"""
Growing trees.
"""
growth_sites = self._rand_bool(self.p_sapling)
self.trees[growth_sites] = True
def advance_one_step(self):
"""
Advance one step
"""
self.grow_trees()
super
调用父类的构造方法。advance_one_step()
,父类中只进行生长,在子类中用 super
调用父类的 advance_one_step()
方法,并添加燃烧的部分。class BurnableForest(Forest):
"""
Burnable forest support fires
"""
def __init__(self, p_lightning=5.0e-6, **kwargs):
super(BurnableForest, self).__init__(**kwargs)
self.p_lightning = p_lightning
self.fires = np.zeros((self.size), dtype=bool)
def advance_one_step(self):
"""
Advance one step
"""
super(BurnableForest, self).advance_one_step()
self.start_fires()
self.burn_trees()
@property
def fire_fraction(self):
"""
Fraction of fires
"""
num_fires = self.fires.sum()
return float(num_fires) / self.num_cells
def start_fires(self):
"""
Start of fire.
"""
lightning_strikes = (self._rand_bool(self.p_lightning) &
self.trees)
self.fires[lightning_strikes] = True
def burn_trees(self):
"""
Burn trees.
"""
fires = np.zeros((self.size[0] + 2, self.size[1] + 2), dtype=bool)
fires[1:-1, 1:-1] = self.fires
north = fires[:-2, 1:-1]
south = fires[2:, 1:-1]
east = fires[1:-1, :-2]
west = fires[1:-1, 2:]
new_fires = (north | south | east | west) & self.trees
self.trees[self.fires] = False
self.fires = new_fires
# 测试父类
forest = Forest()
forest.grow_trees()
print (forest.tree_fraction)
# 测试子类:
burnable_forest = BurnableForest()
burnable_forest.grow_trees()
burnable_forest.start_fires()
burnable_forest.burn_trees()
print (burnable_forest.tree_fraction)
0.0027555555555555554
0.002533333333333333
import matplotlib.pyplot as plt
%matplotlib inline
forest = Forest()
forest2 = BurnableForest()
tree_fractions = []
for i in range(2500):
forest.advance_one_step()
forest2.advance_one_step()
tree_fractions.append((forest.tree_fraction, forest2.tree_fraction))
plt.plot(tree_fractions)
plt.show()
__str__
和 __repr__
中 self.__class__
会根据类型不同而不同:
forest
Forest(size=(150, 150))
forest2
BurnableForest(size=(150, 150))
print (forest)
print(forest2)
Forest
BurnableForest
在 Python
中,鸭子类型(duck typing
)是一种动态类型的风格。所谓鸭子类型,来自于 James Whitcomb Riley
的“鸭子测试”:
当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
假设我们需要定义一个函数,这个函数使用一个类型为鸭子的参数,并调用它的走和叫方法。
在鸭子类型的语言中,这样的函数可以接受任何类型的对象,只要这个对象实现了走和叫的方法,否则就引发一个运行时错误。换句话说,任何拥有走和叫方法的参数都是合法的。
先看一个例子,父类:
class Leaf(object):
def __init__(self, color="green"):
self.color = color
def fall(self):
print ("Splat!")
子类:
class MapleLeaf(Leaf):
def fall(self):
self.color = 'brown'
super(MapleLeaf, self).fall()
新的类:
class Acorn(object):
def fall(self):
print ("Plunk!")
这三个类都实现了 fall() 方法,因此可以这样使用:
objects = [Leaf(), MapleLeaf(), Acorn()]
for obj in objects:
obj.fall()
Splat!
Splat!
Plunk!
这里 fall()
方法就一种鸭子类型的体现。
不仅方法可以用鸭子类型,属性也可以:
import numpy as np
from scipy.ndimage.measurements import label
class Forest(object):
""" Forest can grow trees which eventually die."""
def __init__(self, size=(150,150), p_sapling=0.0025):
self.size = size
self.trees = np.zeros(self.size, dtype=bool)
self.p_sapling = p_sapling
def __repr__(self):
my_repr = "{}(size={})".format(self.__class__.__name__, self.size)
return my_repr
def __str__(self):
return self.__class__.__name__
@property
def num_cells(self):
"""Number of cells available for growing trees"""
return np.prod(self.size)
@property
def losses(self):
return np.zeros(self.size)
@property
def tree_fraction(self):
"""
Fraction of trees
"""
num_trees = self.trees.sum()
return float(num_trees) / self.num_cells
def _rand_bool(self, p):
"""
Random boolean distributed according to p, less than p will be True
"""
return np.random.uniform(size=self.trees.shape) < p
def grow_trees(self):
"""
Growing trees.
"""
growth_sites = self._rand_bool(self.p_sapling)
self.trees[growth_sites] = True
def advance_one_step(self):
"""
Advance one step
"""
self.grow_trees()
class BurnableForest(Forest):
"""
Burnable forest support fires
"""
def __init__(self, p_lightning=5.0e-6, **kwargs):
super(BurnableForest, self).__init__(**kwargs)
self.p_lightning = p_lightning
self.fires = np.zeros((self.size), dtype=bool)
def advance_one_step(self):
"""
Advance one step
"""
super(BurnableForest, self).advance_one_step()
self.start_fires()
self.burn_trees()
@property
def losses(self):
return self.fires
@property
def fire_fraction(self):
"""
Fraction of fires
"""
num_fires = self.fires.sum()
return float(num_fires) / self.num_cells
def start_fires(self):
"""
Start of fire.
"""
lightning_strikes = (self._rand_bool(self.p_lightning) &
self.trees)
self.fires[lightning_strikes] = True
def burn_trees(self):
pass
class SlowBurnForest(BurnableForest):
def burn_trees(self):
"""
Burn trees.
"""
fires = np.zeros((self.size[0] + 2, self.size[1] + 2), dtype=bool)
fires[1:-1, 1:-1] = self.fires
north = fires[:-2, 1:-1]
south = fires[2:, 1:-1]
east = fires[1:-1, :-2]
west = fires[1:-1, 2:]
new_fires = (north | south | east | west) & self.trees
self.trees[self.fires] = False
self.fires = new_fires
class InstantBurnForest(BurnableForest):
def burn_trees(self):
# 起火点
strikes = self.fires
# 找到连通区域
groves, num_groves = label(self.trees)
fires = set(groves[strikes])
self.fires.fill(False)
# 将与着火点相连的区域都烧掉
for fire in fires:
self.fires[groves == fire] = True
self.trees[self.fires] = False
self.fires.fill(False)
C:\Users\26969\AppData\Local\Temp\ipykernel_56208\1569177306.py:2: DeprecationWarning: Please use `label` from the `scipy.ndimage` namespace, the `scipy.ndimage.measurements` namespace is deprecated.
from scipy.ndimage.measurements import label
# 测试
forest = Forest()
b_forest = BurnableForest()
sb_forest = SlowBurnForest()
ib_forest = InstantBurnForest()
forests = [forest, b_forest, sb_forest, ib_forest]
losses_history = []
for i in range(1500):
for fst in forests:
fst.advance_one_step()
losses_history.append(tuple(fst.losses.sum() for fst in forests))
import matplotlib.pyplot as plt
%matplotlib inline
plt.figure(figsize=(10,6))
plt.plot(losses_history)
plt.legend([f.__str__() for f in forests])
plt.show()
__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs。
实例如下:
class JustCounter:
__secretCount = 0 # 私有变量
publicCount = 0 # 公开变量
def count(self):
self.__secretCount += 1
self.publicCount += 1
print (self.__secretCount)
counter = JustCounter()
counter.count()
counter.count()
print (counter.publicCount)
print (counter.__secretCount) # 报错,实例不能访问私有变量
1
2
2
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In [143], line 14
12 counter.count()
13 print (counter.publicCount)
---> 14 print (counter.__secretCount) # 报错,实例不能访问私有变量
AttributeError: 'JustCounter' object has no attribute '__secretCount'
在类的内部,使用 def 关键字来定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数,self 代表的是类的实例。
self 的名字并不是规定死的,也可以使用 this,但是最好还是按照约定使用 self。
__private_method:两个下划线开头,声明该方法为私有方法,只能在类的内部调用 ,不能在类的外部调用。self.__private_methods。
私有方法实例:
class Site:
def __init__(self, name, url):
self.name = name # public
self.__url = url # private
def who(self):
print('name : ', self.name)
print('url : ', self.__url)
def __foo(self): # 私有方法
print('这是私有方法')
def foo(self): # 公共方法
print('这是公共方法')
self.__foo()
x = Site('菜鸟教程', 'www.runoob.com')
x.who() # 正常输出
x.foo() # 正常输出
x.__foo() # 报错
name : 菜鸟教程
url : www.runoob.com
这是公共方法
这是私有方法
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In [144], line 20
18 x.who() # 正常输出
19 x.foo() # 正常输出
---> 20 x.__foo() # 报错
AttributeError: 'Site' object has no attribute '__foo'