Python类的封装(private)和继承(extend)
一、封装
1.概念
广义的封装:函数和类的定义本身,就是封装的体现
狭义的封装:一个类的某些属性,在使用的过程 中,不希望被外界直接访问,而是把这个属性给作为私有的【只有当前类持有】,然后暴露给外界一个访问的方法即可【间接访问属性】
封装的本质:就是属性私有化的过程
封装的好处:提高了数据的安全性,提高了数据的复用性
说明:举例:插排,不需要关心属性在类的内部做了什么样的操作,只需要关心将值传进去,或者将结果获取出来
封装: 函数 => 类 => 模块 => 包
2.私有化属性和私有化方法
如果想让成员变量不被外界直接访问,则可以在属性名称的前面添加两个下划线__,成员变量则被称为私有成员变量
私有属性的特点:只能在类的内部直接被访问,在外界不能直接访问
如果类中的一个函数名前面添加__,则认为这个成员函数时私有化的
特点:也不能在外界直接调用,只能在类的内类调用
代码演示:
# 1.封装
# 封装属性和方法
# python 中没有private protect public
# 类
class Person:
def __init__(self,name, age, sex):
self.name = name # 公有属性
self.__age = age # 私有属性:双下划线开头的属性,只能在当前类使用
self._sex = sex # 公有属性,但不建议这么写
def run(self):
print(self.__age)
self.__eat()
def __eat(self): # 私有方法
print('eat')
# 对象
p = Person('鹿晗', 30, '男')
print(p.name)
# print(p.__age) # 报错,__age是私有属性
print(p._sex)
p.run()
# p.__eat() # 报错,__eat()是私有方法
# 下面的方式可以调用私有属性或私有方法,但不要这么用
# print(p._Person__age)
# p._Person__eat()
3.get函数和set函数,以及@property装饰器
get函数和set函数并不是系统的函数,而是自定义的,为了和封装的概念相吻合,起名为getXxx和setXxx, 这两个函数是用来调用查看和修改私有属性的
get函数:获取值
set函数:赋值【传值】(也是重新赋值到属性中)
@property装饰器
装饰器的作用:可以给函数动态添加功能,对于类的成员方法,装饰器一样起作用
Python内置的@property装饰器的作用:将一个函数变成属性使用
@property装饰器:简化get函数和set函数
使用:@property装饰器作用相当于get函数,同时,会生成一个新的装饰器@属性名.settter,相当于set函数的作用
作用:使用在类中的成员函数中,可以简化代码,同时可以保证对参数做校验
代码演示:
class Person:
def __init__(self,name,wechat):
self.name = name
self.__wechat = wechat
# # 间接获取私有属性 getter
# def get_wechat(self):
# return self.__wechat
#
# # 间接修改私有属性 setter
# def set_wechat(self, new_wechat):
# self.__wechat = new_wechat
@property # 作用:让wechat函数可以当成属性来调用
def wechat(self):
return self.__wechat
@wechat.setter # 让__wechat函数可以赋值修改,等同上面注释的set_wechat函数
def wechat(self, new_wechat):
self.__wechat = new_wechat
@property
def photo(self):
s = self.name + self.__wechat
return s
# 对象
p = Person('刘亦菲', '123')
# print(p.get_wechat())
# p.set_wechat('789')
# print(p.get_wechat())
print(p.wechat)
p.wechat = '456'
print(p.wechat)
print(p.photo)
二、继承【extends】
1.概念
如果两个或者两个以上的类具有相同的属性或者成员方法,我们可以抽取一个类出来,在抽取的类中声明公共的部分
被抽取出来的类:父类,基类,超类,根类
两个或者两个以上的类:子类,派生类
他们之间的关系:子类 继承自 父类
注意:
a.object是所有类的父类,如果一个类没有显式指明它的父类,则默认为object
b.继承可以简化代码,提高代码的复用性
2.单继承和多继承
简单来说,一个子类只能有一个父类,被称为单继承
语法:
父类:
class 父类类名(object):
类体【所有子类公共的部分】
子类:
class 子类类名(父类类名):
类体【子类特有的属性和成员方法】
多继承:一个子类可以有多个父类
语法:
class 子类类名(父类1,父类2,父类3.。。。):# 需要按照需求的顺序来继承
类体
说明:一般情况下,如果一个类没有显式的指明父类,则统统书写为object
代码演示:
# 继承
# object: 根类,超类,顶级的类
# 单继承:只有一个父类
# 多继承:有多个父类
class Ipad(object):
def __init__(self, price):
self.price = price
def movie(self):
print('看电影')
# 子类:派生类
class Iphone(Ipad):
def __init__(self, price, color):
# 需要调用父类的init方法:对父类属性进行初始化
# Ipad.__init__(self, price) # 显式调用
super().__init__(price) # 隐式调用
self.__color = color
# 子类
class Iwatch(Iphone):
def __init__(self, price, color, size):
super().__init__(price, color)
self.size = size
def health(self):
print(self.__color) # 不能使用父类的私有属性和私有方法,私有属性和私有方法不能继承
# # 对象
#
# iphone = Iphone(10000, 'green')
# print(iphone.price, iphone.color)
# iphone.movie()
# iwatch = Iwatch(2000, 'yellow', '1.8')
# print(iwatch.price, iwatch.size)
# iwatch.movie()
# # iwatch.health()
# 多继承
class Father:
def __init__(self, name):
self.name = name
def run(self):
print('会跑步')
# 父类
class Mother:
def __init__(self, age):
self.age = age
def cook(self):
print('会做饭')
# 子类
class Son(Father, Mother):
def __init__(self,name,age,height):
# 显式调用
# Father.__init__(self,name)
# Mother.__init__(self,age)
# 隐式调用
super(Son,self).__init__(name) # 继承Father
super(Father,self).__init__(age) # 继承Mother
self.height = height
# 对象
son = Son('娜扎', 8, 1)
print(son.name,son.age,son.height)
son.run()
son.cook()
# mro算法:从左往右的继承链
print(Son.__mro__)
#(<class '__main__.Son'>,
# <class '__main__.Father'>,
# <class '__main__.Mother'>,
# <class 'object'>)
3.函数重写(overwrite)
在子类中出现和父类同名的函数,则认为该函数是对父类中函数的重写
3.1系统函数重写
str
repr
class Cat:
def __init__(self,name, age):
self.name = name
self.age = age
# 1.必须返回,而且返回字符串
# 2.作用是让打印得对象的值是这里的返回值
def __str__(self):
return f'名字:{self.name},年龄:{self.age}'
# def __repr__(self):
# return f'名字:{self.name},年龄:{self.age}'
cat = Cat('Tom', '3')
print(cat) # 如果没有函数重写: <__main__.Cat object at 0x0000025B7A715828>
# 函数重写后: 名字:Tom,年龄:3
print(repr(cat))
3.2自定义函数重写
函数重写的时机:在继承关系中,如果父类中函数的功能满足不了子类的需求,则在子类中需要重写
# 重写: 方法重写
class Person:
def __init__(self, name):
self.name = name
def jump(self):
print('跳2米')
class Player(Person):
def __init__(self,name):
super().__init__(name)
# 把父类的jump方法重写
def jump(self):
print('跳4米')
# 对象
p = Person('宝强')
p.jump()
p2 = Player('刘翔')
p2.jump()
4 类方法和静态方法
上面的大多数方法都是针对对象而定义使用的,现在看看其他关于类方法的定义:
类方法:
1.可以用类和对象调用,推荐用类调用,可以节省内存,不需要创建就可以调用
2.类方法内部是不可以使用对象属性和其他成员方法或私有方法
3.类方法内部方法内部可以使用其他类方法和类属性
静态方法
1.可以用类和对象调用,推荐用类调用,可以节省内存,不需要创建就可以调用
2.静态方法方法内部是不可以使用对象属性和其他成员方法或私有方法
3.也不建议去使用类属性和类方法,一般写成经常方法的就是一个放在类里面的普通函数
class Dog:
age = 2
def __init__(self, name):
self.name = name
def run(self):
print("成员方法/公有方法")
def __eat(self):
print('私有方法:只有在当前类内部使用')
# 类方法:
# 1.可以用类和对象调用,推荐用类调用,可以节省内存,不需要创建就可以调用
# 2.类方法内部是不可以使用对象属性和其他成员方法或私有方法
# 3.类方法内部方法内部可以使用其他类方法和类属性
@classmethod
def sleep(cls): # cls:class
print('类方法', cls==Dog)
print(cls.age) # 调用类属性
# 静态方法
# 1.可以用类和对象调用,推荐用类调用,可以节省内存,不需要创建就可以调用
# 2.静态方法方法内部是不可以使用对象属性和其他成员方法或私有方法
# 3.也不建议去使用类属性和类方法,一般写成经常方法的就是一个放在类里面的普通函数
@staticmethod
def swim():
print('静态方法')
# 对象
d = Dog('大黄')
d.sleep()
Dog.sleep()
d.swim()
Dog.swim()