一、类与对象介绍
# 面对对象
核心在对象,将程序进行终极整合,对象也是容器,该容器用来存放于同类对象共有的数据与功能
# 类
其实也就是个容器
隐藏属性
1. 怎么隐藏属性
# 隐藏属性其实只需要在属性前加__即可,虽然外部不能直接访问,但是可以间接访问
class People:
def __init__(self, name):
self.__name = name
@property
def name(self):
print(self.__name)
@name.setter
def name(self, val):
if type(val) is not str:
print(‘请输入字符串‘)
self.__name = val
@name.deleter
def name(self):
print(‘不允许删除‘)
obj1 = People(‘Ame‘)
obj1.get_name()
obj1.set_name(‘Yuki‘)
obj1.get_name()
2.为什么隐藏属性
# 我们可以通过隐藏属性来使外界能通过我们的想法去调用这个隐藏属性
property
# property 其实就是装饰器
class People:
def __init__(self, name, height, weight):
self.name = name
self.height = height
self.weight = weight
@property
def blm(self):
return self.weight / (self.height ** 2)
obj1 = People(‘ame‘, 1.75, 85)
print(obj1.blm)
# 在隐藏属性的时候 property的用法
class People:
def __init__(self, name):
self.__name = name
@property
def name(self):
print(self.__name)
@name.setter
def name(self, val):
if type(val) is not str:
print(‘请输入字符串‘)
self.__name = val
@name.deleter
def name(self):
print(‘不允许删除‘)
obj1 = People(‘Ame‘)
# obj1.get_name()
# obj1.set_name(‘Yuki‘)
# obj1.get_name()
obj1.name
obj1.name = ‘Yuki‘
obj1.name
obj1.name
del obj1.name
继承
1.什么是继承
‘‘‘
继承其实就是新创建的类,新创建的子类可以又叫派生类,父类又可以叫基类或者超类
需要注意的是python支持多继承 即一个字累可以有一个父类或者多个父类,子类可以继承父类的数据
ps: python2的继承分为经典式和新式类之分
新式类:继承了object的子类,以及子类的子类子子类
经典类:没有继承了object的子类,以及子类的子类子子类
但是python3里没有这么分,所有的都会默认继承object,所以都是新式类
多继承的优点缺点
优点 子类继承了多父类所有东西,代码能更简洁,更加被利用化
缺点 1 违背了人的正常逻辑,不可能拥有多个“父亲”
2 代码可读性会变得很差
3 不建议用到多继承,有可能会遇到可恶的菱形问题,扩展性也会变差
如果真得一个子类要多继承的话,得使用Mixins
‘‘‘
2.为什么用继承
# 因为要解决类与类之间的代码冗余问题
3.怎么用继承
class School:
school = ‘Oldboy‘
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
class Student(School):
def choose(self):
print(f‘{self.name}正在选课‘)
class Teacher(School):
def __init__(self, name, age, sex, level, salary):
School.__init__(self, name, age, sex)
self.level = level
self.salary = salary
def check(self):
print(f‘{self.name}正在批改作业‘)
stu_obj = Student(‘Ame‘, 18, ‘male‘)
stu_obj.choose()
tea_obj = Teacher(‘Yuki‘, 18, ‘female‘, 10, 10000)
tea_obj.check()
单继承背景下的属性查找
class Foo:
def f1(self):
print(‘Foo.f1‘)
def f2(self):
print(‘Foo.f2‘)
self.f1()
class Bar(Foo):
def f1(self):
print("Bar.f1")
obj = Bar()
obj.f2() # Foo.f2 Bar.f1
# 我们会发现后来没有找到Foo.f1,找到了Bar.f1,因为self是obj给的,所以后来就去Bar里找f1,那么我们怎么找到Foo.f1呢
# 示范一
class Foo:
def f1(self):
print(‘Foo.f1‘)
def f2(self):
print(‘Foo.f2‘)
Foo.f1(self)
# self.f1()
class Bar(Foo):
def f1(self):
print("Bar.f1")
obj = Bar()
obj.f2()
# 示范二
class Foo:
def __f1(self):
print(‘Foo.f1‘)
def f2(self):
print(‘Foo.f2‘)
self.__f1()
class Bar(Foo):
def f1(self):
print("Bar.f1")
obj = Bar()
obj.f2()
菱形问题(钻石问题)
1.什么是菱形问题
class A(object):
def test(self):
print(‘A‘)
class B(A):
def test(self):
print(‘B‘)
class C(A):
def test(self):
print(‘C‘)
class D(B, C):
pass
obj = D()
obj.test() # 像这样的就是菱形问题,D------B, C------A 注意 C--------B, A------object 这样不是菱形问题
# 像这样遇到B, C同级应该会选择B还是C呢,查找方法得依靠C3算法得出来的MRO,注意 python2没有MRO
class A(object):
def test(self):
print(‘A‘)
class B(A):
def test(self):
print(‘B‘)
class C(A):
def test(self):
print(‘C‘)
class D(B, C):
pass
obj = D()
obj.test()
print(D.mro())
‘‘‘
[<class ‘__main__.D‘>, <class ‘__main__.B‘>, <class ‘__main__.C‘>, <class ‘__main__.A‘>, <class ‘object‘>]
就会依靠这个去寻找
注意 1.子类会先于父类被检查
2.多个父类会根据他们在列表中的顺序检查
3.下一类存在多个合法的选择,就会选择第一个父类
‘‘‘
非菱形问题的属性查找顺序
# 经典式与新式类 之间查找顺序都是一样的
菱形问题的属性查找顺序
# 经典类是深度查找,即一路走到黑,找不到再往右边去搜寻
# 新式类是广度查找,即每一路都走,但是不找最后,往右边全部搜寻完了,再去找最后一个
Mixins机制
class Vehicle: # 交通工具
pass
class Helicopter(Vehicle): # 直升飞机
def fly(self):
pass
class CivilAircraft(Vehicle): # 民航飞机
def fly(self):
pass
class Car(Vehicle): # 小轿车
pass
‘‘‘
从这可以看出 飞机们都有飞行的功能,我们应该把飞行功能代码都写入父类,防止代码冗余,但是这样的话
小汽车:???,我也会飞了
所以我们并不能把飞行功能加入交通工具这个父类
‘‘‘
处理办法
class Vehicle: # 交通工具
pass
class Aircraft: # 飞行器
def fly(self):
pass
class Helicopter(Aircraft, Vehicle): # 直升飞机
pass
class CivilAircraft(Aircraft, Vehicle): # 民航飞机
pass
class Car(Vehicle): # 小轿车
pass
‘‘‘
我们加入了个新父类,来解决汽车不能飞,但是飞机能飞的问题,
但是直升飞机和民航飞机就发出了疑问:??怎么我们有了两个亲爹
所以这样是不合人的逻辑了,所以mixin跳了出来,摇了摇头,帮助他们解决了问题
‘‘‘
class Vehicle: # 交通工具
pass
class AircraftMixin: # 飞行器
def fly(self):
pass
class Helicopter(AircraftMixin, Vehicle): # 直升飞机
pass
class CivilAircraft(AircraftMixin, Vehicle): # 民航飞机
pass
class Car(Vehicle): # 小轿车
pass
‘‘‘
mixin其实不是特殊的直白有效的代码,只是一个标志,一个功能的标志,往一个类加入这个Mixin就代表他其实不是真正的父亲,只是一个功能的代表,真正的亲爹是交通工具
同时也要注意mixin用法也有限制!!!!
1. mixin代表的是一个功能,不要代表一个物品,python 对于mixin类的命名方式一般以 Mixin, able, ible 为后缀
2. 然后他的责任很单一,如果有多种类功能,则要写多个mixin,不能挤一块,一个类是可以多继承很多mixin的
3.他不依赖于子类的实现
4.子类就算没有继承mixin,他还是能进行,只不过缺少了功能,比如,飞机没有继承飞,但是他还是可以载客
Mixins是从多个类中重用代码的好方法,但是需要付出相应的代价,我们定义的Minx类越多,子类的代码可读性就会越差,并且更恶心的是,在继承的层级变多时,代码阅读者在定位某一个方法到底在何处调用时会晕头转向
‘‘‘
在子类派生的新方法中重用父类的功能
# 在子类中直接调用父类的功能
class School:
school = ‘Oldboy‘
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
class Teacher(School):
def __init__(self, name, age, sex, level, salary):
School.__init__(self, name, age, sex)
self.level = level
self.salary = salary
# super()调用父类提供给自己的方法----严格依赖继承关系
# 调用super()会得到一个特殊的对象,会自动追寻当前类MRO,去当前父类中找属性
class School:
school = ‘Oldboy‘
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
class Teacher(School):
def __init__(self, name, age, sex, level, salary):
super().__init__(name, age, sex) # 传入的是方法,自动调用对象,所以不用加self
self.level = level
self.salary = salary
tea_obj = Teacher(‘Ame‘, 18, ‘male‘, 10, 10000)
print(Teacher.mro())
print(tea_obj.__dict__)
多态与鸭子类型
1.什么是多态
其实就是一个事物有多种形态
2.在程序中表达多态
class Animal:
pass
class People(Animal):
pass
class Dog(Animal):
pass
class Pig(Animal):
pass
# 动物就有多种形态,称之为多态,那么多态的好处是什么呢,就比如动物都会叫
class Animal:
def say(self):
print(‘动物都会叫....‘)
class People(Animal):
def say(self):
super().say()
print(‘嘤嘤嘤‘)
class Dog(Animal):
def say(self):
super().say()
print(‘汪汪汪‘)
class Pig(Animal):
def say(self):
super().say()
print(‘哼哼哼‘)
obj1 = People()
obj2 = Dog()
obj3 = Pig()
obj1.say()
obj2.say()
obj3.say()
# 好处就是我们在调用子类的时候并不用想子类是什么,只用想着父类就是动物,肯定会叫,这是所有的语言多态的优点,但是python有更好玩的,那就是鸭子类型
3.什么是鸭子类型
其实就是比如 一个人在街上模仿鸭子叫声,走路,甚至还去河里游泳,我们就可以去把这个当成鸭子,代码也一样的道理,如果都没有指定父类,但是他们都有相似之处,就直接当成是一样来看
class Cpu:
def read(self):
print(‘cpu read‘)
def write(self):
print(‘cpu write‘)
class Memory:
def read(self):
print(‘Memory read‘)
def write(self):
print(‘Memory write‘)
class Txt:
def read(self):
print(‘txt read‘)
def write(self):
print(‘txt write‘)
obj1 = Cpu()
obj2 = Memory()
obj3 = Txt()
obj2.write()
obj1.write()
obj3.write()
# 正如cpu Memory都是硬件,TXT则是软件,真要分父类是分开的,但是他们都有读写功能,所以就可以看作一块,这就是鸭子类型
classmethod与绑定方法
1.绑定方法
特殊之处在于将调用者本身传入给第一个参数
# 绑定给对象的方法 调用者是对象 传入的也是对象
class Mysql:
def __init__(self, ip, port):
self.ip = ip
self.port = port
obj1 = Mysql(‘1,0,1,0‘, 3306)
# 绑定给类的方法 调用者是类,传入的也是类
import setting
class Mysql:
def __init__(self, ip, port):
self.ip = ip
self.port = port
def func(self):
print(f‘{self.ip}{self.port}‘)
@classmethod
def from_conf(cls):
return cls(setting.IP, setting.PORT)
obj1 = Mysql.from_conf()
print(obj1.__dict__)
# 在setting文件里放了IP 和 PORT,直接这里调用
staticmethod与非绑定方法
# 非绑定方法其实就是静态方法,当一个函数不需要对象也不需要类的时候,但是想放在类下面,就用@staticmethod装饰器
# 没有绑定给任何人,调用者可以是类 可以是对象 不会有自动传入参数的效果
class Mysql:
def __init__(self, ip, port):
self.ip = ip
self.port = port
@staticmethod
def creat_id():
import uuid
return uuid.uui4()
反射
# 什么是反射
反射就是一个对象(不见棺材不落泪)到最后才知道他是什么属性
# 为什么要用反射
因为假如在一个类中,传入了对象,然后想找到对象的属性,用反射就很容易找到
# 怎么用反射
class School(object):
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print(f‘<{self.name},{self.age}>‘)
obj = School(‘Ame‘, 10)
# 假如在以后的工作中,你的同事写好了代码跟你交接,你只知道有个对象obj,该怎么知道obj的属性是什么呢
# 要用到dir()方法
print(dir(obj)) # [‘__class__‘, ‘__delattr__‘, ‘__dict__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, .......]
# dir()会显示出obj所有的属性,所以在运用反射的4个内置函数便OK
print(hasattr(obj, ‘name‘)) # hasattr 是判断有没有这个属性 True
print(getattr(obj, ‘name‘)) # getattr 是调出这个属性的值 Ame
setattr(obj, ‘name‘, ‘Yuki‘)
print(obj.name) # setattr 是修改这个属性 Yuki
delattr(obj, ‘name‘) # delattr 删除这个属性
# 用途案例
class People(object):
def put(self):
print("lalala")
def get(self):
print("lblblb")
def interactive(self):
methon = input(‘------>‘)
if hasattr(self, methon):
print(self, methon)
else:
print(‘指令不存在‘)
obj = People()
obj.interactive()
# 从这可以看出,发射就是用来判断未知的对象属性的
内置方法
# 什么是内置方法
以__开头__结尾的就是内置方法
特点 会自动触发调用
# 为什么用内置方法
为了定制化我们的类或者对象
# __str__ :打印对象时,自动触发,然后会将返回值显示出来
class School:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f‘{self.name},{self.age}‘
obj = School(‘Ame‘, 18)
print(obj) # Ame,18 由此可以看出来 直接打印对象,然后就会输出__srt__
# __del__:删除的时候会自动调用
class School:
def __init__(self, name, age):
self.name = name
self.age = age
def __del__(self):
print(‘lalala‘)
obj = School(‘Ame‘, 18)
print("-------------")
‘‘‘
-------------
lalala
‘‘‘
# 从输出结果可以看 lalala自动打印了出来 这是为什么,因为程序结束后,pycharm要回收空间,所以会自动删除,然后__del__就打印了出来,如果在print之前删除呢
class School:
def __init__(self, name, age):
self.name = name
self.age = age
def __del__(self):
print(‘lalala‘)
obj = School(‘Ame‘, 18)
del obj
print("-------------")
‘‘‘
lalala
-------------
‘‘‘
# 所以__del__是删除的时候会自动调用,那么这个用途在哪的,我们在一个程序结束后,pycharm会自动回收,但是只是回收自己的空间,操作系统的并没有回收,所以__del__是用来告诉pycharm回收系统空间的
元类
# 引入
一切皆是对象
# 什么是元类
用来实例化产生的类的类
关系: 元类-------实例化-------类---------实例化--------对象
# 查看内置的元类:我们用class关键字定义的所有的类以及内置的类都由内置的元类type实例化产生的
class的机制
# class其实是由以下的步骤产生
class_name = ‘People‘
class_bases = (object,)
class_dir = {}
class_body = ‘‘‘
def __init__(self, name, age):
self.name = name
self.age = age
def __del__(self):
print(‘lalala‘)
‘‘‘
exec(class_body, {}, class_dir)
print(type(class_name, class_bases, class_dir)) # <class ‘__main__.People‘>
# 这种方法是自定义元类的精髓
# 其实平时 class People 的People 并不是类, 只是个变量名而已, 其中的功能代码集体才是一个类
自定义元类
class Mymeta(type):
def __init__(self,x ,y ,z): # 这边必须要传四个参数, 因为class people的存在会把class过程的参数传给Mymeta
print(‘123‘)
class People(metaclass=Mymeta):
def __init__(self, name, age):
self.name = name
self.bases = age
def say(self):
print(f‘<{self.name},{self.age}>‘)