面向对象
编程范式
面向过程编程
面向过程:核心是过程二字,过程指的是解决问题的步骤
优点:复杂的问题流程化,进而简单化
缺点:可扩展性差
面向对象编程
面向对象:核心是对象二字,对象就是特征与技能的结合体
优点:可扩展性强
缺点:编程复杂度高
类
类:类就是一系列对象相似的特征与技能的结合体
类与对象的区别
对象是类的实例化
类是相同结构的对象的抽象
面向对象的三大特点
1、封装,在类中数据的赋值、内部调用对外部用户是透明的
2、继承,一个类可以派生出子类,子类自动添加父类的属性和方法
3、多态,一个接口,多种实现
定义类和实例化出对象
# 先定义类
class LuffStudent:
shcool = 'luffcity'
def learn(self):
print('is learning')
def eat(self):
print('is eating')
def sleep(self):
print('is sleep')
# 后定义对象
stu1 = LuffStudent() # 实例化一个对象
stu2 = LuffStudent()
stu3 = LuffStudent()
print(stu1)
print(stu2)
print(stu3) # 返回一个对象
类的使用
class LuffStudent:
school = 'luffcity'
def learn(self):
print('is learning')
def eat(self):
print('is eating')
# 查看类的命名空间 返回一个字典
print(LuffStudent.__dict__)
# 查
# print(LuffStudent.school)
# 增
LuffStudent.county = 'chaina'
print(LuffStudent.county)
# 删
del LuffStudent.county
# 改
LuffStudent.school = 'Luffcity'
print(LuffStudent.school)
对象的使用
# __init__ 方法用来为对象定制对象自己独有的特征
class LuffStudent:
school = 'luffcity'
def __init__(self, name, sex, age):
self.name = name
self.sex = sex
self.age = age
def learn(self):
print('is learning')
def eat(self):
print('is eating')
# 产生对象
stu1 = LuffStudent('nida','M',18)
print(stu1.__dict__)
# 加上__init__方法后,实例化的步骤
'''
1、先产生一个空对象
2、调用LuffStudent.__init__(stu1,'lida','M',18)
'''
# 查属性
# print(stu1.__dict__)
# 改属性
# stu1.name = 'tom'
# print(stu1.name)
# 删属性
# del stu1.sex
# print(stu1.__dict__)
# 增属性
# stu1.class_name = 'Python'
# print(stu1.class_name)
属性查找与绑定方法
class LuffStudent:
school = 'luffcity'
def __init__(self, name, sex, age):
self.name = name
self.sex = sex
self.age = age
def learn(self):
print('%s is learning' % (self.name))
def eat(self):
print('%s is eating' % (self.name))
stu1 = LuffStudent('王二丫', '女', 18)
stu2 = LuffStudent('李三泡', '男', 38)
stu3 = LuffStudent('张铁蛋', '男', 48)
# 对象:特征与技能的结合体
# 类:类是一系列对象相似的特征与相似的技能的结合体
'''
类中的数据属性:所有对象共有的
print(LuffStudent.school,id(LuffStudent.school))
print(stu1.school,id(stu1.school))
print(stu2.school,id(stu2.school))
print(stu3.school,id(stu3.school))
他们的命名空间都指定到了一个命名空间中
'''
# 类中的函数属性:是绑定给对象的,绑定到不同的对象是不同的绑定方法,对象调用绑定方法时,会把对象本身当作第一个参数传入,传给self
print(LuffStudent.learn)
print(stu1.learn)
print(stu2.learn)
print(stu3.learn)
继承(什么是什么的关系)
'''
继承,是指类与类之间的关系
'''
class Parentclass1():
pass
class Parentclass2():
pass
class SubClass1(Parentclass1):
pass
class SubClass2(Parentclass1, Parentclass2):
pass
# print(SubClass1.__bases__) # 查看继承了那些父类
# print(SubClass2.__bases__)
'''
属性查找小练习
'''
class Foo:
def f1(self):
print('from Foo.f1')
def f2(self):
print('from Foo.f2')
self.f1()
class Bar(Foo):
def f1(self):
print('from Bar.f2')
b = Bar()
b.f2() # 这个打印的b.f1()
print(Bar.mro()) # 打印继承的执行的顺序
继承的实现原理
# python2.* 新式类,经典类
# python3.* 新式类
# 在python2中——》经典类:没有继承object的类型,以及它的子类都称为经典类
'''
class Foo:
pass
class Bar(Foo):
pass
'''
# 在python3中——》新式类:继承object的类型,以及它的子类都称为新式类
# 在python3--> 新式类,一个类没有继承object类,默认就继承object
'''
class Foo(object):
pass
class Bar(Foo):
pass
'''
'''
经典类: 深度优先查找,从左到右,一查到底
新式类: 广度优先查找,平行查找
'''
在子类中重用父类的方法或属性
# 在子类派生出的新的方法中重用父类的方法,有两种实现方式
# 方式一、指名道姓 不依赖继承
# 方式二、 super() 依赖继承
class Hero(object):
def __init__(self, nickname, life_value, aggresivity):
self.nickname = nickname
self.life_value = life_value
self.aggresivity = aggresivity
def attack(self, enemy):
enemy.life_value -= self.aggresivity
class Garen(Hero):
camp = 'Demacia'
def __init__(self, nickname, life_value, aggresivity, weapon):
super().__init__(nickname, life_value, aggresivity) # super() python3中可以不用写类和self
self.weapon = weapon
def attack(self, enemy):
# Hero.attack(self, enemy) # 指名道姓方法 不依赖继承方法
super(Garen,self).attack(enemy) # 依赖继承
print('form Garen class')
g = Garen('草丛轮', 100, 30, '大宝剑')
print(g.__dict__)
组合(什么有什么的关系)
class People:
school = 'luffycity'
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
class Teacher(People):
def __init__(self, name, age, sex, level, salary):
super().__init__(name, age, sex)
self.level = level
self.salary = salary
def teach(self):
print('%s is teaching' % self.name)
class Course():
def __init__(self, course_name, course_price, course_period):
self.course_name = course_name
self.course_price = course_price
self.course_period = course_period
def tell_info(self):
print('课程名<%s> 课程价钱<%s> 课程周期<%s>' % (self.course_name, self.course_price, self.course_period))
class Student(People):
def __init__(self, name, age, sex, class_time):
super().__init__(name, age, sex)
self.class_time = class_time
def learn(self):
print('%s is lenrning' % self.name)
class Date:
def __init__(self, year, mon, day):
self.year = year
self.mon = mon
self.day = day
def tell_info(self):
print('%s-%s-%s' % (self.year, self.mon, self.day))
python = Course('python', 3000, '3moms')
linux = Course('linux', 2000, '4mons')
teacher1 = Teacher('alex', 18, 'male', 10, 3000)
teacher1.course = python
student1 = Student('张三', 28, 'female', '08:30:00')
d = Date(1988, 4, 20)
student1.birh = d
student1.birh.tell_info()
# student1.course1 = python
# student1.course2 = linux
# student1.course1.tell_info()
# student1.course2.tell_info()
# student1.courses = []
# student1.courses.append(linux)
# student1.courses.append(python)
# student1.courses[1].tell_info()
抽象类
import abc
'''
abc模块实现一个抽象类的规范
'''
class Animal(metaclass=abc.ABCMeta): # 这是一个抽象类 这个抽象类只能被继承不能实例化
@abc.abstractmethod
def run(self):
pass
@abc.abstractmethod
def eat(self):
pass
# animal = Animal()
class People(Animal):
def run(self):
print('people is walking')
def eat(self):
print('people is eating')
class Pig(Animal):
def run(self):
print('pig is walking')
def eat(self):
print('pig is eating')
class Dog(Animal):
def run(self):
print('dog is walking')
def eat(self):
print('dog is eating')
peo1 = People()
pig1 = Pig()
dog1 = Dog()
peo1.run()
多态
'''
多态:同一类型事物的多种形态
'''
import abc
class Animal(metaclass=abc.ABCMeta): # 同一类事物:动物
@abc.abstractmethod
def talk(self):
pass
class People(Animal): # 动物的形态之一:人
def talk(self):
print('say hello')
class Dog(Animal): # 动物的形态之二:狗
def talk(self):
print('say wangwang')
class Pig(Animal): # 动物的形态之三:猪
def talk(self):
print('say aoao')
# 多态性:指定是可以在不考虑对象的类型的情况下1而直接使用对象
peo1 = People()
dog1 = Dog()
pig1 = Pig()
# 增加了程序的灵活性
# 以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)
def func(animal):
animal.talk()
func(peo1)
# 增加了程序额可扩展性
# 通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用   
# 鸭子类型
# Python崇尚鸭子类型,即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子’
# 二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用
class TxtFile:
def read(self):
pass
def write(self):
pass
class DiskFile:
def read(self):
pass
def write(self):
pass
封装之如何隐藏属性
class A:
__x = 1
def __init__(self, name):
self.__name = name
def __foo(self):
print('run foo')
def bar(self):
self.__foo()
print('from bar')
# print(A.__dict__) # 可以看出 __x这个变量进行了一个变形 变成了_A__x
# a= A('alex')
# a.bar()
'''
这种变形的特点:
1、在类外部无法直接obj.__AttrName
2、在类内部是可以最近使用:obj.AttrName
3、 子类无法覆盖父类__开头的属性
'''
class Foo():
def func(self):
print('from foo')
class Bar(Foo):
def func(self):
print('from bar')
# b = Bar()
# b.func()
'''
总结这种变形需要注意的问题:
1、这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
2、变形的过程只在类的定义是发生一次,在定义后的赋值操作,不会变形
3、变形这个过程是在内定义的时候就已经发生了
4、在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
'''
class B:
__x = 1 # _B__x = 1
def __init__(self, name):
self.__name = name # _B__name
# 验证问题1
# print(B._B__x)
# 验证问题2,3
# B.__y = 2
# print(B.__y) # 内定义结束之后就没有隐藏属性的这个说法了
# 验证问题4
class A():
def __foo(self):
print('A.foo')
def bar(self):
print('A.bar')
self.__foo() # 这个打印的是_A__foo()
self.foo() # 这个打印的是B.foo()
class B(A):
def foo(self):
print('B.foo')
b = B()
b.bar()
封装的意义
'''
1、封装数据属性:明确的区分内外,控制外部对隐藏的属性的操作行为
'''
class Peoplee:
def __init__(self, name, age):
self.__name = name
self.__age = age
def tell_info(self):
print('Name:<%s> Age:<%s>' % (self.__name, self.__age))
def set_info(self, name, age):
if not isinstance(name, str):
print('名字必须是字符类型')
return
if not isinstance(age, int):
print('名字必须是数字类型')
return
self.__name = name
self.__age = age
# p = Peoplee('egon', 18)
# p.set_info('EGON',28)
# p.tell_info()
# 封装方法: 隔离复杂度,
class ATM:
def __card(self):
print('插卡')
def __auth(self):
print('用户认证')
def __input(self):
print('输入取款金额')
def __print_bill(self):
print('打印账单')
def __take_money(self):
print('取款')
def withdraw(self):
self.__card()
self.__auth()
self.__input()
self.__print_bill()
self.__take_money()
a = ATM()
a.withdraw() # 用户直接访问即可 不用管ATM的工作流程
封装与可扩展性
class Room:
def __init__(self, name, owner, weight, length,height):
self.name = name
self.owner = owner
self.__weight = weight
self.__length = length
self.__height = height
def tell_area(self):
return self.__weight * self.__length * self.__height
r = Room('卫生间', 'alex', 10, 10,10)
print(r.tell_area()) # 用户只需要访问接口即可 不管我内部的变量怎么变化
property的使用
'''
BMI指数 (bmi是计算而来的)
算法
体制指数(BMI)=体重(kg)+身高^2(m)
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常胖,高于32
EX: 70kg/(1.75*1.75) = 22.86
'''
class People:
def __init__(self, name, height, weight):
self.name = name
self.height = height
self.weight = weight
@property # 一个伪装装饰器 可以将一个方法伪装成数据类型返回,但是本质上还是一个方法
def bmi(self):
return self.weight / (self.height ** 2)
p = People('eaon', 1.81, 75)
print(p.bmi)
property的针对使用
class People:
def __init__(self, name):
self.__name = name
@property # 针对属性的查看行为
def name(self):
return self.__name
@name.setter # 针对属性的修改行为 setter是关键字
def name(self, val):
if not isinstance(val, str):
print("名字必须是个字符串")
return
self.__name = val
@name.deleter # 针对属性的删除行为 是关键字deleter
def name(self):
print('不允许输出')
p = People('egon')
print(p.name) # 查看
p.name = 'EGON' # 修改
print(p.name)
del p.name # 删除
绑定方法与非绑定方法介绍
'''
在类内部定义的函数,分为两大类:
1、绑定方法:绑定给谁,就应该由谁来调用,谁来调用就会把调用者当作第一个参数自动传入
绑定到对象的方法:在类内定义的没有被任何装饰器修饰的方法
绑定到类的方法: 在类内定义的被classmethod装饰器修饰的方法
2、非绑定方法:没有自动传值的说法,就是在类中定义了应该普通的员工方法工具,类也可以实现
非绑定方法:不与类或者对象绑定,被staticmethod装饰器修饰的
'''
class Foo:
def __init__(self, name):
self.name = name
def tell(self): # 没有被任何装饰器修饰的就是绑定到对象的放到
print('名字是%s' % self.name)
@classmethod # 被classmethod修饰过的就是绑定到类的方法
def func(cls): # cls=Foo
print(cls)
@staticmethod
def func1(x, y):
print(x + y)
f = Foo('egon')
# f.tell()
# print(Foo.func)
# print(Foo.func1)
# print(f.func1)
Foo.func1(1,2)
f.func1(3,4)
绑定方法与非绑定方法的使用
import settings
import hashlib
import time
class People:
def __init__(self, name, age, sex):
self.id = self.create_id()
self.name = name
self.age = age
self.sex = sex
def tell_info(self): # 绑定到对象的方法
print('Name:%s Age:%s Sex:%s' % (self.name, self.age, self.sex))
@classmethod
def from_cinf(cls):
obj = cls(
settings.name,
settings.age,
settings.sex,
)
return obj
@staticmethod
def create_id():
m=hashlib.md5(str(time.time()).encode('utf-8'))
return m.hexdigest()
# p = People('egon', 18, 'male')
# 绑定给对象,就应该由对象来调用,自动将对象本身当作第一个参数传入
# p.tell_info()
# 绑定给类,就应该由类来调用,自动将类本身当作第一个参数传入
# p = People.from_cinf() # from_cinf(People)
# p.tell_info()
# 非绑定方法,不与类或者对象绑定,没有自动传值怎么一说
p1 = People('alex',18,'M')
p2 = People('Alex',28,'M')
p3 = People('clex',38,'M')
print(p1.id)
print(p2.id)
print(p3.id)
反射
# 反射;通过字符串映射到对象的属性
class People:
def __init__(self, name, age):
self.name = name
self.age = age
def talk(self):
print('%s is talking' % self.name)
obj = People('egon', 18)
choice = 'name'
# print(obj.choice) # 字符串形式无法调用类型
hasattr(obj, choice) # 可以通过这个内置方法查看这个字符串在类字典中是否存在 obj.__dict__中
print(getattr(obj,choice,None)) # None的作用当不存在这个属性时返回应该None
setattr(obj,choice,'male') # 通过字符串的方式修改属性
print(obj.name)
delattr(obj,choice) # 通过字符串的方式删除属性
print(obj.__dict__)
反射的应用
class Service:
def run(self):
while True:
inp = input('>>>:').strip() # 当和用户交互时input返回的是一个字符串类型
cmds = inp.split()
if hasattr(self, cmds[0]): # 判断用户输入的cmd有没有这个方法
func = getattr(self, cmds[0]) # 传入方法
func(cmds)
def get(self,cmds):
del cmds[0]
print('get....',cmds)
def pull(self,cmds):
del cmds[0]
print('pull...',cmds)
obj = Service()
obj.run()
内置方法isinstance() 和 issubclass()
# isinstance() # 检查是否obj是否是类 cls 的对象
class Foo(object):
pass
obj = Foo()
print(isinstance(obj, Foo)) # 存在返回一个True
# issubclass() # 检查sub类是否是 super 类的派生类
class Foo1(object):
pass
class Bar(Foo1):
pass
print(issubclass(Bar, Foo1)) # 存在返回一个True
内置方法item系列 将对象模拟成一个字典,进行操作
# item系列 ,将对象模拟成一个字典,进行操作
class Foo():
def __init__(self, name):
self.name = name
def __getitem__(self, item): # 查
return self.__dict__.get(item)
def __setitem__(self, key, value): # 改
self.__dict__[key]=value
def __delitem__(self, key): # 删
del self.__dict__[key]
obj = Foo('egon')
# print(obj['name']) # 查看属性
obj['sex'] = 'male' # 设置属性
print(obj.__dict__)
del obj['sex']
print(obj.__dict__)
内置方法str
class People:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return '<name:%s,age:%s>' % (self.name, self.age) # 必须是一个字符串
obj = People('egon', 18)
print(obj) # obj.__str__()
# __str__ 定义完后或者打印的时候触发,并且以返回值的方式返回,返回的对象必须是一个字符串
内置方法del
# __del__ 在程序结束后自动回收系统资源,如果不加python不会回收系统资源
# del 可以手动回收系统资源
# python 自动回收机制,只会回收python程序中的资源,不会回收操作系统支援
class Open:
def __init__(self, filename):
print('Open file ....')
self.filename = filename
def __del__(self): # 类结束后触发
print('回收操作系统资源')
f = Open('settings.py')
print('---main---')
exec的使用方法
'''
exec的使用
参数1:字符串形式的命令
参数2:全局作用域(字典形式),如果不指定默认使用globals()
参数3:局部作用域(字典形式),如果不指定默认使用locals()
'''
g = {'x': 1, 'y': 2}
l = {}
exec('''
global x,m
x = 10
m = 100
z = 3
''', g, l)
print(g)
print(l)
元类
一切皆对象,对象可以怎么用?
1、都可以被引用,x=obj
2、都可以当作函数的参数传入
3、都可以当作函数的返回值
4、都可以当作容器类型的元素, l=[func,time,obj,1]
定义一个类的方式
方式1:class
定义类的两种方式
方式一:class
class Chinese:
country = 'China'
def __init__(self, name, age):
self.name = name
self.age = age
def talk(self):
print('%s is taking' % self.name)
Zhangsan = Chinese('张三',18)
Zhangsan.talk()
方式2:type
# 定义类的三要素 (1、类名, 2、类的基类,3、类的名称空间)
class_name = 'Chinese' # 类名
class_bases = (object,) # 类的基类
# 类的属性
class_boby = '''
country = 'China'
def __init__(self, name, age):
self.name = name
self.age = age
def talk(self):
print('%s is taking' % self.name)
'''
class_dic = {} # 类的名称空间
exec(class_boby, globals(), class_dic)
# print(class_dic)
Chinese = type(class_name,class_bases,class_dic)
Zhangsan = Chinese('张三',18) # 实例化
Zhangsan.talk()
使用元类来控制类的行为
'''
定制元类来控制类的行为
'''
# raise 直接抛出异常
class Mymeta(type):
def __init__(self, class_name, class_bases, class_dic):
if not class_name.istitle():
raise TypeError('类名的首字母必须大写')
if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
raise TypeError('必须有注释,而且注释不能为空')
super(Mymeta, self).__init__(class_name, class_bases, class_dic)
class Chinese(object, metaclass=Mymeta): # metaclass指定元类 默认为type
'''
aaa
'''
country = 'China'
def __init__(self, name, age):
self.name = name
self.age = age
def talk(self):
print('%s is taking' % self.name)
call 方法控制类的实例化行为
# __call__方法
class Foo:
def __call__(
class Foo:
def __call__(self, *args, **kwargs):
print(self)
print(args)
print(kwargs)
obj = Foo()
obj(1, 2, 3, 4, a=1, b=2)
# 元类内部也应该有一个__call__方法,会调用Foo时触发执行,整个方法可以控制类的实例化行为
自定义元类控制类的实例化行为
class Mymeta(type):
def __init__(self, class_name, class_bases, class_dic):
if not class_name.istitle():
raise TypeError('类名的首字母必须大写')
if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
raise TypeError('必须有注释,而且注释不能为空')
super(Mymeta, self).__init__(class_name, class_bases, class_dic)
def __call__(self, *args, **kwargs): # 相当于:obj=Chinese('张三', 18)
# 第一件事:先造一个空对象
obj = object.__new__(self)
# 第二件事:产生对象
self.__init__(obj, *args, **kwargs)
# 第三件事:返回对象
return obj
class Chinese(object, metaclass=Mymeta): # metaclass指定元类 默认为type
'''
aaa
'''
country = 'China'
def __init__(self, name, age):
self.name = name
self.age = age
def talk(self):
print('%s is taking' % self.name)
obj = Chinese('张三', 18)
print(obj.__dict__)
单例模式
# 单例模式
'''
当应该类反复被调用时,为了可以节省内存,就是可以使用单例模式来运行
'''
class MySQL:
__instance = None
def __init__(self):
self.host = '127.0.0.1'
self.port = 3306
# 这就是一个单例
@classmethod
def singleton(cls):
if not cls.__instance:
obj = cls()
cls.__instance = obj
return cls.__instance
def conn(self):
pass
def execute(self):
pass
obj1 = MySQL.singleton()
obj2 = MySQL.singleton()
print(obj1 is obj2) # 他们指向的内存id都是一个
方式二使用元类来控制
class Mymeta(type):
def __init__(self, class_name, class_bases, class_dic):
super(Mymeta, self).__init__(class_name, class_bases, class_dic)
self.__instance = None
def __call__(self,*args,**kwargs): # 相当于:obj=Chinese('张三', 18)
if not self.__instance:
obj = object.__new__(self)
self.__init__(obj)
self.__instance = obj
return self.__instance
class MySQL(object,metaclass=Mymeta):
def __init__(self):
self.host = '127.0.0.1'
self.port = 3306
def conn(self):
pass
def execute(self):
pass
obj1 = MySQL()
obj2 = MySQL()
print(obj1 is obj2)