类的命名
遵守变量命名的规范 大驼峰命名(由一个单词或者多个单词构成,首字母大写) 尽量避免跟系统命名相似的命名如何声明一个类
必须用class关键字
类由属性和方法构成,其他不允许出现
成员属性定义可以直接使用变量赋值,如果没有值,允许使用None
访问成员对象
使用点操作符 obj.成员属性名称 obj.成员方法 可以通过默认内置变量检查类和对象的所有成员 对象所有成员检查 obj._dict 类的所有成员检查 class_name._dict'''
定义一个学生类,用来形容学生
'''
class Student():
#一个空类,pass代表跳过
#pass必须有
pass
#定义一个对象
zs = Student()
#再定义一个类,用来描述听Python的学生
class PythonStudent():
name = None
age = 19
course = "Python"
def doHomeWork(self):
print(self.name)
print("我在写作业")
#推荐在函数末尾使用return返回
return None
def goHome(self):
print("我要回家啦")
return None
#实例化一个对象,具有具体的功能
yueyue = PythonStudent()
yueyue.name = "yueyue"
yueyue.doHomeWork()
#类和对象的成员检测
yueyue.__dict__
PythonStudent.__dict__
类和对象的成员分析
类和对象都可以存储成员,成员可以归类所有,也可以归对象所有
类存储成员时使用的是与类关联的一个对象
独享存储成员是存储在当前对象中
对象访问一个成员时,如果对象中没有该成员,尝试访问类中的同名成员,如果对象中有此成员,一定使用对象中的成员
创建对象的时候,类中的成员不会放入对象中而是得到一个空对象,没有成员
通过对象对类中成员重新赋值或者通过对象添加成员时,对应成员会保存在对象中,而不会修改类成员
class A():
name = "A"
pass
#获取A中name的值
print(A.name)
#打印出A.name指向的内存地址
print(id(A.name))
#实例化一个A的对象
a = A()
#打印出a中name的值
print(a.name)
#打印出a.name指向的内存地址
print(id(a.name))
#判断两个id是否相等
if id(a.name) == id(A.name):
#如果两个id是相等的,那么就将a.name赋值为b
a.name = "b"
#接着判断两个id是否相等
if id(a.name) != id(A.name):
#经过上面的赋值如果两个id不相等,就进入到这里面
print("进入到这里面就说明,在给a这个对象赋值的时候,"
"python又重新给a分配了一块内存空间现在a不是和A指向的同一个实例,"
"这就得出了结论'对象访问一个成员时,如果对象中没有该成员,尝试访问类中的同名成员,如果对象中有此成员,一定使用对象中的成员'")
else:
print("两个内存地址不相等")
self
self在对象的方法中表示当前对象本身,如果通过对象调用一个方法,那么该对象会自动传入到当前方法的第一个参数中
self不是关键字,只是一个用于接收对象的普通参数,理论上可以用任何一个普通变量名代替
方法中有self形参的方法成为非绑定类的方法方法,可以通过对象访问,没有self的是绑定类的方法,只能通过类访问
#self案例
class A():
#创建一个有self的方法
def aA(self):
print("这里是aA")
#创建一个没有self的方法
def bB():
print("这里是bb")
#实例化a
a = A()
#访问A类中的aA方法,aA方法是有self参数的,默认将a作为参数,传进去
a.aA()
#访问A类中的bB方法,bB方法是没有self参数的,如果通过对象a进行调用的话,会默认将a作为参数传进去,会报错
#如果使用类实例来调用的话,直接调用的是A.bB()不需要传入任何参数
A.bB()
使用类访问绑定类的方法时,如果类方法中需要访问当前类的成员,可以通过____class___.成员名来访问
#访问参数案例
class B():
b = "b"
def b1(self):
#将传入对象的b属性重新赋值为"B"
self.b = "B"
print(self.b)
def b2(self):
self.b = "a"
#直接通过__class__.b来访问类中成员的数据
print(__class__.b)
b = B()
b.b1()
b.b2()
鸭子模型
在python中,如果A类拥有和B类一样的方法,当有一个函数调用B类,并利用到B类的方法,我们传入的A类一样可以运行,函数不会检查对象的类型是不是A类,只要A类拥有B类的方法和属性,就可以正确的被调用
#鸭子模型
class Duck():
name = "鸭子"
#鸭子的翅膀是黄色
wing = "黄色"
#鸭子会游泳
def swimming(self):
print("我是一只{0},我会游泳".format(self.name))
class Angel():
name = "天使"
wing = "白色"
def swimming(self):
print("我是{0},我会游泳".format(self.name))
def fly(self):
print("我是天使我,会飞")
du = Duck()
#将对象本身传值进去
du.swimming()
#直接使用类实例调用鸭子会飞的方法,但是将对象a作为值放进去
Duck.swimming(du)
#直接使用类实例调用鸭子会飞的方法,但是将angle类实例,作为对象传进去
Duck.swimming(Angel())
面向对象的三大特性
封装
封装就是对对象的成员进行访问限制
封装的三个级别
公开:public 受保护的:protected 私有的:private public,protected,private不是关键字判别对象的位置
对象内部 对象外部 子类中私有
私有成员是最高级别的封装,只能在当前类或当前对象中访问
在成员前面添加两个下划线即可
class Person():
#names是共有成员
name = "zhangsan"
#__age是私有 成员
__age = 18
python的私有不是真私有,是一种成为name mangling的改名策略,可以使用对象._classname__attributename(对象.____类名__变量名)访问
#私有访问案例
class Person():
#共有的姓名
name = "张三"
#私有化年龄
__age = 18
def say(self):
print("我叫{0},我今年{1}岁了".format(self.name,self.__age))
p = Person()
#直接赋值,不受影响
p.name = "李四"
#通过对象._类名__私有化变量名访问
p._Person__age= 98
p.say()
受保护的封装protected
受保护的封装是将对象成员进行一定级别的封装,然后再类中或者子类中都可以进行访问,但是在外部不可以 封装方法:在变量名称加一个下划线公开的,公共的 public
公开的成员没有任何操作,任何地方都可以访问 继承继承就是一个类可以获得另外一个类中的成员属性和成员方法
作用:减少代码,增加代码的复用功能,同时可以设置类与类的直接的关系
继承与被继承的概念
被继承的累叫做父类,也叫基类,也叫超类
用于继承的类叫子类,也叫派生类
继承与被继承一定存在一个is-a关系
在python中任何一个类都会有一个父类-Object
继承的语法:
#继承的语法
#创建一个Person的类
class Person():
#人都会有名字,姓名
name = "我是人"
age = None
#人都会睡觉
def sleep(self):
print("我该睡觉了")
#创建一个Teacher类
#Teacher类继承Person类
class Teacher(Person):
#该类不做任何定义
pass
#实例化一个Tacher类对象
t = Teacher()
#直接在实例化对象t中调用name属性
print(t.name)
#直接在类实例中调用name属性
print(Teacher().name)
#Person类直接调用name属性
print(Person().name)
继承的特征
所有类都继承自object类,即所有类都是object类的子类
子类一旦继承父类,则可以使用父类除出私有成员外的所有属性和方法
子类继承父类后并没有将父类成员完全赋值到子类中,而是通过引用关系访问调用
#子类继承父类后并没有将父类成员完全赋值到子类中,而是通过引用关系访问调用
te = Teacher()
#获取实例化对象t中调用name属性
print(id(te.name))
#获取类实例中调用name属性
print(id(Teacher().name))
#获取Person类直接调用name属性的id
print(id(Person().name))
if id(te.name) == id(Teacher().name) == id(Person().name):
print("#子类继承父类后并没有将父类成员完全赋值到子类中,而是通过引用关系访问调用")
子类中可以定义独有的成员属性和方法
##子类可以定义独有的属性和方法
class Person():
#定义变量name
name = None
#定义变量age
age = None
#创建selfIntrodution()[自我介绍]方法
def selfIntrodution(self):
print("我叫{0},我今年{1}".format(self.name,self.age))
#定义一个Student类继承Person类
class Student(Person):
#学生要具有,姓名,年龄,学号
name = None
age = None
#学号是Student独有的属性
id = None
def doHomeWork(self):
print("我是{0},我要做作业".format(self.id))
s = Student()
s.name = "张三"
s.age = 18
s.id = 9527
#调用子类中独有的方法和属性
s.doHomeWork()
# >控制台输出
# 我是9527,我要做作业
子类中定义的成员和父类的成员如果相同,则优先使用子类成员
#子类中定义的成员和父类的成员如果相同,则优先使用子类成员
class Person():
#定义变量name
name = None
#定义变量age
age = None
#创建selfIntrodution()[自我介绍]方法
def selfIntrodution(self):
print("我叫{0},我今年{1}".format(self.name,self.age))
#定义一个Student类继承Person类
class Student(Person):
#学生要具有,姓名,年龄,学号
name = None
age = None
#学号是Student独有的属性
id = None
def doHomeWork(self):
print("我是{0},我要做作业".format(self.id))
#实例化一个Student的对象
s = Student()
#给变量中的属性赋值
s.name = "张三"
s.age = 18
s.id = 9527
#调用父类的自我介绍方法
s.selfIntrodution()
#>控制台输出
#>我叫张三,我今年18
子类如果想扩充父类的方法,可以在定义新方法的同时访问父类成员来进行代码重用,可以使用 父类.父类成员 的格式来调用父类成员,也可以使用super().父类成员的格式来调用
继承变量的查找顺序问题
优先查找自己的变量 自己没有变量则查找父类 构造函数中如果本类没有定义,则查找父类构造函数 如果本类构造函数有定义,则不再继续向上查找 构造函数是一类特殊的函数,在每次实例化的时候第一次被调用
主要工作内容是初始化,所以得名
语法
#构造函数语法
class Dog():
#__init__()函数就是构造函数
def __init__(self):
print("这是狗狗的构造函数")
如果定义了构造函数,在实例化的时候使用自己的构造函数,不查找父类构造函数
如果没定义,则自动查找父类构造函数
如果子类没定义,而父类的构造函数带参数,则按照父类构造函数参数来构造
super super不是关键字,而是一个类 super的作用是获取MRO(MethodResolustionOrder)列表中的第一个类 super和父类没有任何实质性关系,但是可以通过super可以调用父类 super的两个方法,参见构造函数调用父类的构造函数 可以利用__类名__._mro 单继承和多继承单继承:每个类只能继承一个类
多继承:每个类允许继承多个类
单继承和多继承的优缺点
单继承 优点:传承有序逻辑清晰简单隐患少 多点:功能不能拓展,只能在当前唯一的继承链中拓展 多继承 优点:类的功能扩展方便 缺点:继承关系混乱菱形继承/钻石继承
多个子类继承自同一个父类,这些子类又被同一个类继承,于是继承关系图就形成一个菱形图谱
#创建一个类A
class A():
pass
#创建一个类B继承A
class B(A):
pass
#创建一个类C继承A
class C(A):
pass
#创建一个类D继承B,C
class D(B,C):
pass
关于多继承的MRO
MRO就是多继承中,用于保存继承顺序的一个列表 Python本身就是采用C3算法来多继承的菱形继承进行计算结果 MEO列表计算原则 子类永远在父类前面 如果多个父类,则根据继承语法中括号内的书写顺序存放 如果多个类继承了同一个父类,子孙类中只会选取继承语法括号中第一个父类的父类 多态 多态就是同一个对象在不同情况下有不同的状态出现 多态不是语法,是一种设计思想 多态性:多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。#创建一个Worker(工人)类
class Worker(Person):
def work(self):
print("我在盖房子")
#创建一个Teacher类
class Teacher(Person):
def work(self):
print("我在教书")
#创建一个Police类
class Police(Person):
def work(self):
print("我在执行公务")
#创建一个接口,传入一个任意类型的值
def fun(obj):
obj.work()
wor = Worker()
tea = Teacher()
pol = Police()
#开始调用不同的方法
fun(wor)
fun(tea)
fun(pol)
#其中工人类,教师类,警察类是Person类的多种形态
多态:同一种事物的多种形态,动物分人类,猪类,狗类
Mixin设计模式
主要采用多继承方式对类进行扩展 我们使用多继承的语法来实现Mixin 使用Mixin实现多继承的时候非常小心 首先他必须表示某一单一功能,而不是某个物品 职责必须单一,如有多个功能则写多个Mixin Mixin不能依赖于子类的实现 子类即使没有继承这个Mixin类,也能照样工作,只是缺少了某个功能 优点 使用Mixin可以在不对类进行任何修改的情况下,扩充功能 可以方便的组织和维护不同功能组件的划分 可以根据需要任意调整功能类的组合 可以避免创建很多新的类,导致类的继承混乱类相关函数
issubclass:检测一个类是不是另一个的子类
#issubclass
class A():
pass
class B(A):
pass
#检测B是不是A的子类
print(issubclass(B,A))
isinstance:检测一个对象是否是一个对象的实例
#isinstance案例
class A():
pass
class B(A):
pass
#检测a是不是A的实例
a = A()
print(isinstance(a,A))
hasattr:检测一个属性是否在一个对象里
#hasattr
class A():
c = None
class B(A):
pass
#检测一个c属性是否在a对象里
a = A()
print(hasattr(a,"c"))
gettattr:get attribute
settattr:set attribute
deltattr:delete attribute
dir:获取对象的成员列表
class A():
c = None
a = A()
print(dir(a))
类的成员描述符(属性)
类的成员描述符是为了类中对类的成员属性进行相关操作而创建的一种方式
get:获得属性 set:给属性赋值 delete:删除属性如果想使用类的成员描述符,大概有三种方法
使用类实现描述器
使用属性修饰符
使用property函数
#创建Student类
class Student():
def __init__(self,name):
self._name = name
#property的语法
#属性 = property(fget(),fset(),fdel(),"doc")
#创建get方法
def sget(self):
print("进行查询操作...")
return self._name
#创建set方法
def sset(self,name):
print("进行修改操作...")
self._name = name.upper()
#创建del方法
def sdel(self):
print("进行删除操作...")
self._name = "NoName"
#创建property方法
name = property(sget,sset,sdel,"这个是属性操作")
#实例化一个Student对象
s = Student("lisi")
#先获取name的值
print(s.name)
#讲name赋值
s.name = "zhangsan"
#再次获取name的值
print(s.name)
#删除name的值
del s.name
print(s.name)
类的内置属性
_ _ dic _ _ :以字典的形式显示类的成员组成
print(Student.__dict__)
_ _ doc _ _:获取类的说明文档
print(Student.__doc__)
_ _ name _ _:获取类的名称
print(Student.__name__)
_ _ bases _ _:获取类的父类,以元组的形式组成
print(Student.__bases__)
类的魔术方法
魔术方法就是不需要人为调用的方法,基本是在特定的时刻自动触发
魔术方法的统一特征,方法名被前后两个下划线包裹
__ init __:构造函数
__ new __:对象实例化方法,此函数比较特殊,一般不需要使用
__ call __:对象当函数使用时触发
class A():
def __init__(self):
print("我是构造函数")
#测试__call__函数
def __call__(self):
print("我被当做属性调用了")
#实例化一个a对象
a = A()
a()
__ str __:当对象被当作字符串使用时调用
class A():
def __init__(self):
print("我是构造函数")
#测试__str__函数
def __str__(self):
return "我是a的注释"
#实例化一个a对象
a = A()
print(a)
__ rept __ :返回字符串,跟 __ str __具体区别百度
属性操作相关__ getattr __:在获取一个不存在的属性是触发
class A():
name = None
def __getattr__(self,a):
print("对不起,{0}这个属性没有".format(a))
a = A()
print(a.c)
__ setattr __:对成员属性进行设置的时候触发
参数:
self用来获取当前对象 被设置的属性名称,以字符串的形式出现 需要对属性名称设置的值作用
进行属性设置的时候进行验证或者修改\class A():
name = None
def __setattr__(self,key,value):
super().__setattr__(key,value)
a = A()
a.name = "zhangsan"
print(a.name)
类和对象的三种方法
实例方法
需要实例化对象才能使用的方法
静态方法
不需要实例化,通过类直接访问
类方法
不需要实例化,通过类直接访问
class Person():
#实例方法
def eat(self):
print(self)
print("实例方法调用")
#类方法
#类方法用 @classmethod声明
@classmethod
def paly(cls):
print(cls)
print("类方法调用")
@staticmethod
def run():
print("实例方法调用")
zhangsan = Person()
#调用实例方法
zhangsan.eat()
#调用类方法
Person.paly()
#用对象调用类方法
zhangsan.paly()
#调用静态方法
Person.run()
#用对象调用静态方法
zhangsan.run()
抽象方法
没有具体实现内容的方法叫做抽象方法
抽象方法的主要意义是规范子类的行为和动作
抽象类的使用需要借助abc模块
import abc抽象类
包含抽象方法的类叫做抽象类,也成为abc类
抽象类的使用
抽象类可以包含抽象方法,也可以包含具体方法 抽象类中可以有方法也可以有属性 抽象类不可以被直接实例化 必须继承才可以使用,且继承的子类必须实现所有继承的抽象方法 假定子类没有实现所有继承的抽象方法,则子类也不能实例化 抽象类的主要作用是设定类的标准,以便于开发的时候具有统一的规范import abc
class Q(metaclass=abc.ABCMeta):
#抽象实例方法
@abc.abstractmethod
def a(self):
pass
#抽象类方法
@abc.classmethod
def b(cls):
pass
#抽象实例方法
@abc.staticmethod
def d():
pass
自定义类
类其实是一个类定义和各个方法的自由组合
可以定义类和函数,然后自己通过类直接赋值
可以借助于MethodType实现
借助于Type实现
#第一种方法
class A():
pass
def b(self):
print("b方法")
#将b方法扔进A类中
A.b = b
a = A()
a.b()
#第二中方法
from types import MethodType
class A():
pass
def b(self):
print("b方法")
a = A()
a.b = MethodType(b,A)
a.b()
#第三种方法
def sayHello(self):
print("Hello")
#使用type来创建一个类
C = type("cName",(object,),{"sayHello":sayHello})
c = C()
c.sayHello()