编程范式
面向过程编程:
优点:复杂的问题流程化,进而简单化
缺点:可扩展性差
面向对象编程:
优点:扩展性好
缺点:编程复杂度高
如何编写面向对象的程序
1、将函数归类+提取公共值(反推)
2、在指定类中编写和当前类相关的所有代码+提取公共值
面向对象的三大特性:封装/继承/多肽
封装
1、将相关功能封装到一个类中,封装方法:目的是隔离复杂度
2、将数据封装到一个对象中,封装数据,对外提供操作该数据的接口,然后我们可以在接口附加上对该数据操作的限制,以此完成对数据属性操作的严格控制。
继承
为何要有继承========为了提高代码的重用性
父类(基类)
子类(派生类)
原则:先在自己类中找,没有就去父类找
支持多继承,先左后右
python支持多继承,Java、Php等都不支持
多态
多种形态或多种状态
鸭子模型:只要可以嘎嘎叫就是鸭子
意义:由于python自带原生就是多肽,所以没有特殊性
在Java中,参数是需要定义数据类型的,这样在传参的时候,就必须是规定数据类型的对象或者是规定数据类型派生类的对象,这就是参数的多种形态,简称多肽。而python,不需要规定参数的数据类型,只要他能执行就可以。
python原生支持多肽,崇尚鸭子模型。由于python函数传参时,无需指定类型:
def func(arg): # arg可以是多种类型,只要其中有send方法即可。
arg.send()
重点
1、编写方式和执行流程
2、如何归类?反向:归类+提取公共值。正向:类相关的功能+提取公共值
3、三大特性
面向对象能帮你解决什么问题
1、归类,将相关函数封装到一个类中
2、将数据打包封装到一个对象中
self到底是谁
self参数是python帮助我们自动传递。
如果执行面向对象中的方法时,前面必须有一个对象,self就是这个对象。
构造方法
创建对象时自动执行
实例变量/字段
类的成员分三类
1、变量(字段)
——实例变量(字段)
————公有的实例变量(字段)
————私有的实例变量(字段)====找一个内部人func,让func帮你执行内部私有__name
——类变量(静态字段)
————公有的类变量(字段)
————公有的类变量(字段)
准则:实例变量(字段)访问时,使用对象访问。即obj1.name
类变量(静态字段)访问时,使用类方法。即Foo.country (实在不方便,才使用对象)
什么时候用类变量:当所有对象中有共同的字段,且要改都改,要删都删时,可以将实例变量提取到类变量。
2、方法
实例方法====方法中会调用构造函数的变量才有必要写
静态方法(@staticmethod)====如果方法中无需使用对象中封装的值,那么就可以使用静态方法
——方法上方写@staticmethod
——方法参数可有可无
——调用可以类调用,也可以对象调用
类方法(@classmethod)====想用当前类,才写类方法,自动传递当前类为第一个参数
——方法上方写@classmethod
——调用可以类调用,默认会将当前类传递到参数中
——如果在方法中会使用到当前类,那么就可以使用类方法
class Foo:
@classmethod
def func1(cls):
print(cls)
@staticmethod
def func2(a):
print(a)
方法同样也有私有方法,类似于变量。
3、属性(通过方法改造出来的)
编写:在方法上方加 @property,
调用:在调用时就不用加括号
应用场景:对于简单的方法,无需传参,且有返回值时,就可以使用@property
面试题:静态方法和实例方法的区别?
定义:类方法和静态方法要加自己的装饰器,而实例方法不用
调用:类方法和静态方法通过类.调用,实例方法先实例化,再调用
应用场景:如果在方法内部不会用到对象相关数据时,用静态方法或者类方法,否则用实例方法。如果方法内部要使用当前类,则使用类方法会自动传入当前类,更方便。
嵌套
一个对象里嵌套另一个对象
类或对象能否做字典的key====可以
总结:
1、对象中封装了什么?
2、self到底是谁?
主动调用其他类的成员(在子类派生出的新的方法中重用父类的方法)
方式一:
指名道姓的调用,不依赖继承
class Foo1:
def f1(self):
print('五个功能')
class Foo2:
def f2(self):
print('三个功能')
Foo1.f1(self) # 主动调用其他类的方法,与继承无关
方式二:
super().f1() # 按照当前类的继承顺序,找下一个f1,不是简单的找父类
class Foo:
def f1(self): # self是Info的对象
super().f1() # 按照类的继承顺序,找下一个,self是Info的对象,找到Bar中的f1执行
print('三个功能')
class Bar:
def f1(self):
print('六个功能')
class Info(Foo,Bar):
pass
obj = Info()
obj.f1()
这两种方式的区别是:方式一是跟继承没有关系的,而方式二的super()是依赖于继承的,并且即使没有直接继承关系,super仍然会按照mro继续往后查找
类中的内置方法
1、__init__=======类名()====自动执行 __init__ (构造方法) # 为对象赋值,做初始化
2、__call__=======对象()====自动执行 __call__
3、__getitem__======对象【】====自动执行 __getitem__
4、__setitem__=========对象【】=val ==== 自动执行 __setitem__ (无返回值)
5、__delitem__========del 对象【】 ==== 自动执行 __delitem__ (无返回值)
6、__add__===========对象+对象 ==== 自动执行 __add__
7、__enter__======__exit__====with 对象 as f ==== 自动执行 __enter__ 和 __exit__ ,一个开始,一个结束,f是enter的返回值
8、__new__========真正的构造方法 ==== __new__ # 真正的创建一个空对象
9、__str__ ==== 返回值必须是一个字符串,在终端打印对象时,打印这个返回值
10、__doc__ ==== 获取这个对象所在类的注释
11、__dict__ ==== 将对象中封装的值以字典的形式打印出来
12、__iter__ ==== 返回一个迭代器或生成器,有这个方法的类,对象才能被迭代。
如果想要把不可迭代的对象转变为可迭代的对象
1、在类中定义__iter__方法
2、iter内部返回一个迭代器(生成器也是一种特殊的迭代器)
# 8、真正的构造方法 ==== __new__ # 真正的创建一个空对象
class Foo(object):
def __init__(self, a): # 往对象里面放东西
self.a = a
def __new__(cls, *args, **kwargs): # 这个方法实际上是创建一个空对象
return object.__new__(cls) # python内部创建一个当前类的对象(初创对象时空的)
面向对象相关
1、isinstance/issubclass/type
isinstance() 第一个参数是对象,第二个参数是类 检查第一个参数是否是第二个参数(及父类)的对象
issubclass(a,b) 两个参数都是类 检查第一个参数是否是第二个参数的派生类
type() 获取当前对象是有哪个类创建的
2、方法和函数
print(函数名)
print(obj.xxx)
from types import MethodType, FunctionType
def check(arg):
if isinstance(arg, MethodType):
print(arg,'是一个方法')
elif isinstance(arg, FunctionType):
print(arg,'是一个函数')
else:
print('不知道是什么')
总结:有对象调用时方法,无对象调用时函数。
3、反射
基础部分的重点:迭代器,生成器,装饰器,列表推导式,面向对象,反射
v=getattr(obj,“func”) # 根据字符串(第二个参数),去对象中寻找与之同名的成员
hasattr # 根据字符串(第二个参数),判断对象中是否有成员
setattr # 根据字符串(第二个参数),第三个参数为设置的变量或函数,动态设置一个成员 (内存)
delattr # 根据字符串(第二个参数),动态删除一个成员(内存)
以上所有第一个参数可以是模块,类或者对象。
from types import FunctionType
import demo01
val = input('请输入要执行的函数')
if hasattr(demo01,val):
func_or_val = getattr(demo01,val) # 根据字符串为参数,去对象中寻找与之同名的成员
if isinstance(func_or_val, FunctionType):
func_or_val()
else:
print(func_or_val)
else:
print('不存在的属性名')
模块也可以反射:
import sys
def s1():
print 's1'
def s2():
print 's2'
this_module = sys.modules[__name__]
hasattr(this_module, 's1')
getattr(this_module, 's2')
问题:你见过的什么后面可以加括号?
类()
对象()==== 对象要有__call__方法
函数()
obj.方法()
以上的所有都可以被调用。可以通过callable()判断。
问题:匿名函数是否可以放在类中?
约束
Base类用于约束,约束其派生类必须编写一个方法,不然执行可能就会报错
class BaseMessage(object):
def send(self):
"""
必须继承BaseMessage,然后其中必须编写send方法,用于完成具体的业务逻辑
:return:
"""
raise NotImplementedError('.send()必须被重写')
# raise Exception('.send()必须被重写') 这样写是不专业的
import abc
class Base(metaclass=abc.ABCMeta):
@abc.abstractmethod
def save_db(self):
pass
python:类
Java、c#:接口,类
接口中不允许在方法内部写代码,只能约束继承他的类必须实现接口中定义的是有方法。
类又分为类和抽象类,抽象类中有抽象方法。
抽象类,用于约束,约束继承他的派生类必须实现他其中的抽象方法。
总结:
1、什么事接口以及作用?
接口是一种数据类型,主要用于约束派生类中必须实现指定的方法。
python中不存在,Java和c#中是存在的。
2、python中使用什么来约束呢?
(1)抽象类+抽象方法 ——编写上麻烦
(2)认为主动抛出异常——简洁
3、约束是抛出的异常是否可以用其他的呢?
专业的写法:raise NotImplementedError(’.send()必须被重写’)
不专业的写法:raise Exception(’.send()必须被重写’)
4、以后看代码,揣摩写代码人的心思
5、写代码:raise NotImplementedError(’.send()必须被重写’)
6、应用场景:多个类内部都必须有某些方法时,需要使用基类+异常进行约束
一切皆对象
1.都可以被引用
2.都可以当作函数的参数传入
3.都可以当作函数的返回值
4.都可以当作容器类的元素
元类
产生类的类称之为元类。
定义类的两种方式:
方式一:class
方式二:type
类的三要素:1.类名。2.类的基类们。 3.类的名称空间
class_name = 'Chinese'
class_bases = (object,)
class_body = """
country = 'china'
def __init__(self,name,age):
self.name = name
self.age = age
def talk(self):
print('%s is talking' %self.name)
"""
class_dic = {}
exec(class_body,globals(),class_dic)
Chinese = type(class_name,class_bases,class_dic)
自定义异常业务逻辑复杂的情况下使用
1、如何自定义异常类?
2、如何主动抛出异常?
3、如何捕获异常?
class MyException(Exception):
def __init__(self, code, msg):
self.code = code
self.msg = msg
try:
raise MyException(1000,'操作异常')
except KeyError as obj:
print(obj)
except MyException as obj:
print(obj)
except Exception as obj:
print(obj)
加密(hashlib模块)
import hashlib
def md5(pwd):
obj = hashlib.md5(b'shiqian') # 实例化对象 加盐
obj.update('admin'.encode('utf-8')) # 加密的必须是字节
v = obj.hexdigest() # 获取密文
return v
# e5b1948d78e22233663b5ca9df7a3da6
username = input('请输入用户名:')
pwd = input('请输入密码:')
if username == 'shiqian' and md5(pwd) == 'e5b1948d78e22233663b5ca9df7a3da6':
print('登录成功')
关键词:撞库,加盐
面向对象多继承
python2
经典类:
新式类:如果自己或自己的前辈,只要有人继承object,那么此类就是新式类
python3
新式类:python3全部都是新式类,类似于全部继承object
经典类和新式类查找成员的顺序不一样
经典类:一条道走到黑。(深度优先)
新式类:通过c3的算法实现。 print(Foo.mro) 通过这个方法查找继承的顺序。super是遵循.__mro__执行顺序。
c3算法:获取第一个表头和其他表尾进行比较,不存在则拿走,如果存在,则放弃,然后获取第二个表的表头和其他表尾进行比较。(简单的就留个根,根最后找。)
为何还是不会写面向对象的程序
领域建模:找名词,加属性,连关系