示例代码如下
class Dog():
def __init__(self,name,age):
self.name = name
self.age = age
# 定义公共方法
def bark(self):
print(f"{self.name} can bark")
class pipi(Dog):
def play(self):
print(f"{self.name} 会打滚")
class dandan(Dog):
def other(self):
print(f"{self.name} 会杂技")
dog1 = pipi("皮皮",2)
dog2 = dandan("蛋蛋",3)
dog1.play()
dog1.bark() # 调用公共的方法
dog2.other()
我们在上面的代码找那个分别编写了 pipi 类 和 dandan 类,它们分别继承于 Dog 类,因此他们都会具备 Dog 类具有的属性和方法,然后他们也可以拥有他们独有的属性和方法
假设有这种情况,子类和父类拥有同样的方法名,但是我们调用的方法是属于父类的还是子类的呢?我们改一下代码看看。
class Dog():
def __init__(self,name,age):
self.name = name
self.age = age
def bark(self):
print(f"{self.name} can bark")
def eat(self):
print(f"{self.name} 喜欢吃鸡肉")
class pipi(Dog):
def play(self):
print(f"{self.name} 会打滚")
def eat(self):
print(f"{self.name} 喜欢吃火腿")
dog1 = pipi("皮皮",2)
dog1.eat()
运行结果:
皮皮喜欢吃火腿
所以我们发现,当子类和父类方法同名时,子类方法会覆盖父类的方法。
当子类没有 init 方法的时候,它会直接继承 父类的 init 方法
当子类 定义了 init 方法的时候,在子类的 init() 中调用父类的 init()方法,和上面情况类似,区别就是调用父类 init() 方法的时机
子类定义了 init(),子类__init__() 中没有调用父类的方法,这时候注意,父类的私有属性无法调用,子类调用私有属性的 get 和 set 方法会报错
封装的核心:封装是隐藏对象中一些不希望被外部访问到的属性或方法
在学习 Java 的过程中,我们进行封装操作的时候,设置属性的访问权限为 private(只在当前类可以访问),所以我们会使用 getter 和 setter 方法来修改属性的值。在 Python 中我们也可以使用 同样的 getter 和 setter 方法
3.3.1 封装程度(**)
class Dog():
# 安全性问题,属性值不能乱改
def init(self,name,age):
# 方式一,换一个属性名保存
self.hidden_name = name
self.hidden_age = age
def getAge(self):
return self.hidden_age
def setAge(self, age):
if age < 0 or age > 150:
raise ValueError('年龄值不合格')
self.hidden_age = age
虽然我们这样做, 还是不希望 hidden_xx 属性被外部访问,因此使用更高级的封装
class Retangle():
# 内部访问,使用 hidden 任然可以被访问
# 使用 __作为私有属性,是外部不可以被访问
def __init__(self,width, height):
self.__width = width
self.__height = height
def setWidth(self,width):
self.__width = width
def getWidth(self):
return self.__width
def setHeight(self,height):
self.__height = height
def getHeight(self):
return self.__height
我们使用 ‘__’ 作为隐藏属性,使外部不可见,这也是很常用的一种方法,
接下来解释一下 双下划线的作用:
在 Python 中,双下换线是作为隐藏属性而存在的,但它其实还是可以通过方法访问的到的,在 Python 内部当中,双下划线实际上是把 属性换了一个更复杂的方式表示,比 hidden_属性 更复杂,它其实是把 __xxx 替换成了 _类名__属性名 表示。
所以 Python 中的封装一般做到这一步就差不多了
使用装饰器把 getter 和 setter 更好的封装
class Person():
'''
__xxxx 成为隐藏属性
__name -> _Person__name
使用 _xxx 作为私有属性,没有特殊需求,不要修改私有属性
类一般使用属性或方法不可见可以使用单下划线
'''
# 使用一个
def __init__(self,name):
self.__name = name
@property
def name(self):
return self.__name
'''
setter
getter 方法更好的使用
@property,将一个 get 方法,转换为对象属性
@属性名.setter 讲一个 set 方法,转换为对象属性
两者缺一不可
'''
# setter 方法的的装饰器: @属性名.setter
@name.setter
def name(self, name):
self.__name = name
p = Person('猴赛雷')
p.name = 'aaa' # 使用属性的方式 调用 setter 和 getter
print(p.name)
多态:它指的是一个声明为 A类型的变量,可能是指 A类型的对象,也可以是 A类型的任何子类对象
class Dog():
def __init__(self,name,age):
self.name = name
if age > 100:
raise ValueError("狗狗的年龄不可能这么大")
self.__age = age
def getAge(self):
return self.__age
def setAge(self,age):
if age > 100:
raise ValueError ("狗狗的年龄不可能这么大")
self.__age = age
# 定义公共方法
def bark(self):
print(f"{self.name} can bark")
def eat(self):
print(f"{self.name} 喜欢吃鸡肉")
class pipi(Dog):
def play(self):
print(f"{self.name} 会打滚")
def eat(self):
print(f"{self.name} 喜欢吃火腿")
# 方法覆盖
def bark(self):
print(f"{self.name} 在叫,嘻嘻....")
class dandan(Dog):
def other(self):
print(f"{self.name} 会杂技")
def dog_bark(dog):
if isinstance(dog, Dog):
dog.bark()
d = Dog("阿拉斯加",3)
dog_bark(d)
d1 = pipi("小皮",2)
dog_bark(d1)
d2 = dandan("蛋蛋",1)
dog_bark(d2)
在上面的代码中,我们定义了一个 dog_bark() 方法,它可以介绍父类的对象,也可以接受子类的对象
使用多态,我们并不需要给每一个 子类定义一个调用 bark() 的方法,pipi_bark(), dandan_bark(),只需要定义一个 dog_bark(), 在调用的时候给它传递对应的子类对象即可。
面向对象三大特征
1.封装(把一些重要属性封装起来,防止被查看)