目录
class 类名(继承自)
的方式定义一个新类;__init__
函数登场了,在类的的函数中,默认会传入参数self
,因为Python的构造函数只能有一个,所以参数的传入要比较谨慎的书写,可以通过关键字参数
传入参数的方式来实现。数据封装
这个面向对象的三大特点之一,即将对对象的属性的操作放在类内部的函数中,即类的方法
中,在类的各个方法中实现对属性的操作。访问限制
问题,这就涉及到和java中的private型的属性一个道理了,当然在Python中实现更加简洁,只要通过属性的名称即可判断是否为private型,即在属性名的开头加上__
双下划线即可,且结尾不能跟着下划线。_类名__属性名
(实例名前为一个下划线,属性名前为两个下划线)的方式来调用;且在修改属性值时,我们可能错误的用实例名.__属性名= 新的值
的方式来修改实例中的属性值,但这本身并没有真正的修改成功,反而是新建了一个变量且为它赋值了。所以上面的两种方法都是不可靠的,都是禁止使用的。object
,这是所有类最后都要继承的对象,而这个新的class我们就称为object的子类(subclass)
,而被继承的object这个class则称为基类
、父类
或超类
(Base class、Super class)。来一个简单的实例来说明一下继承的优点:
例2.1.2.1:
# !user/bin/python
# coding=utf-8
__author__ = "zjw"
class Shape(object):
def describe(self):
print "我是一个形状"
class Rectangle(Shape):
pass
class Circle(Shape):
pass
if __name__ == '__main__':
shape = Shape()
rectangle = Rectangle()
circle = Circle()
# 分别调用三个实例的describe()
shape.describe()
rectangle.describe()
circle.describe()
输出:
我是一个形状
我是一个形状
我是一个形状
分析:
Rectangle
和Circle
这两个类继承自Shape
类,我们在父类Shape
中实现了describe方法,但是两个子类中并没有真正的实现该方法。可以在main函数中看到,当我们实例了三个对象后,两个子类对象调用describe方法时,实际上是直接调用了父类的describe方法。Rectangle
和Circle
虽然没有写describe方法,但是他们继承了Shape
,那么在实例化后,他们也可以直接调用父类的describe方法。类比总结:
首先我们对上面的例子进行小小的修改,为两个子类加上describe方法:
例2.2.1.1:(这里只贴出修改部分)
class Rectangle(Shape):
def describe(self):
print "我是一个长方形"
class Circle(Shape):
def describe(self):
print "我是一个圆圈"
输出:
我是一个形状
我是一个长方形
我是一个圆圈
分析:可以看到当我们为子类添加上describe方法后,子类的describe方法就将父类的覆盖掉了,后面当我们实例化子类时,调用到的就是子类的describe。这就说明了继承的另一个好处,多态
。
先来一个实例看看:
例2.2.2.1:
# !user/bin/python
# coding=utf-8
__author__ = "zjw"
class Shape(object):
def describe(self):
print "我是一个形状"
def area(self):
pass
def perimeter(self):
pass
class Rectangle(Shape):
def __init__(self, length, width):
self.__length = length
self.__width = width
def describe(self):
print "我是一个长方形"
def area(self):
return self.__length * self.__width
def perimeter(self):
return 2 * (self.__length + self.__width)
if __name__ == '__main__':
shape = Shape()
shape.describe()
print shape.area()
print shape.perimeter()
rectangle = Rectangle(2, 2)
rectangle.describe()
print rectangle.area()
print rectangle.perimeter()
输出:
我是一个形状
None
None
我是一个长方形
4
8
分析:
Shape
为Rectangle
的父类,在父类中的area方法和perimeter方法并没有内容,而在Rectangle方法中我们则写了相应的内容,来输出该形状的面积和周长。在main函数的实践中,可以看到子类的方法已经覆盖了父类的方法,且调用时可以实现不同的功能。Shape
父类,并重写一下area和perimeter方法,即可轻松实现方法的覆盖。再来一个直观实例展示多态的好处:
例2.2.2.2:
# !user/bin/python
# coding=utf-8
__author__ = "zjw"
class Shape(object):
def describe(self):
print "我是一个形状"
def twice_describe(self):
# isinstance来判断类型
print "是否是一个Shape?", isinstance(self, Shape)
print "是否是一个Rectangle?", isinstance(self, Rectangle)
print "是否是一个Circle?", isinstance(self, Circle)
self.describe()
self.describe()
print
class Rectangle(Shape):
def describe(self):
print "我是一个长方形"
class Circle(Shape):
def describe(self):
print "我是一个圆圈"
if __name__ == '__main__':
shape = Shape()
rectangle = Rectangle()
circle = Circle()
# 分别调用三个实例的describe()
shape.twice_describe()
rectangle.twice_describe()
circle.twice_describe()
输出:
是否是一个Shape? True
是否是一个Rectangle? False
是否是一个Circle? False
我是一个形状
我是一个形状是否是一个Shape? True
是否是一个Rectangle? True
是否是一个Circle? False
我是一个长方形
我是一个长方形是否是一个Shape? True
是否是一个Rectangle? False
是否是一个Circle? True
我是一个圆圈
我是一个圆圈
分析:
这里请注意看父类中的twice_describe
方法,在这个方法里,我们首先通过isinstance
函数来判断self的类型,接着我们调用两次self.describe()
来输出此时的形状,最后一个print起到再换行的作用。
我们可以看到父类的实例shape只是一个Shape,而子类的rectangle实例不仅是一个Rectangle还是一个Shape,子类的circle实例同理。这让我想起java中继承我们要遵循子类 is a 父类
原则。
接着说这样写的好处:通过该多态形式的实现,我们将每个子类不同的方法写在子类自己的内部,而将统一的方法写在父类中,父类中这个统一的方法可以调用子类中不同的方法,那么我们就可以实现代码的精简。我们不必在子类中再一一写统一的那个方法,直接调用父类的方法即可完美的实现。为什么这么说呢,当我们再想来一个椭圆类,我们想在椭圆类的describe方法中写下一句“你好,我是椭圆形”。这时我们调用父类的twice_describe方法依旧是可行的。就不会说同一个方法,我们在每个子类中都重写一遍,那么有成百上千个子类,那怎么办?那么这就体现了多态的好处啦。
通过该例子,我们来说一下多态的真正的威力:调用方只管调用,不管细节,而当我们新增一种Shape
子类时,我们只要确保describe()
方法编写正确即可,不用管原来的代码是如何调用的。这就是著名的开闭原则
:
对扩展开放:允许新增Shape
子类;
对修改封闭:不需要修改依赖Shape
类型的twice_describe()
等函数。
self
变量实现的。我们前面接触到的属性应都属于实例属性。类属性属于类所有,所有实例共享一个属性。
来个实例说明一下:
例3.1:
# !user/bin/python
# coding=utf-8
__author__ = "zjw"
class Test(object):
count = 0
def __init__(self):
Test.count = Test.count + 1
if __name__ == '__main__':
test1 = Test()
test2 = Test()
test3 = Test()
test4 = Test()
test5 = Test()
print test1.count
print test2.count
print test3.count
print test4.count
print test5.count
print Test.count
test1.count = 0
print "此时的test1的count值时:", test1.count
输出:
5
5
5
5
5
5此时的test1的count值时: 0
分析:
self.count
来调用类属性count的,因为在上面我们说过,这样调用就是实例的属性了,而通过Test.count
即可调用到此时的类属性count,并进行赋值。test1.count = 0
语句,目的是说明当我们定义了一个实例属性和类属性名发生冲突是,类属性会被覆盖掉,输出的就是实例属性的值。所以要避免类属性名和实例属性名的冲突问题。原文:https://www.cnblogs.com/vanishzeng/p/12246305.html