一, 面向对象编程与面向过程编程对比
1, 面向过程编程: 核心过程二字, 过程指的是解决问题的步骤, 既先干什么, 再干什么, 后干什么, 基于该思想的编程就好比在生产一条流水线, 是一种机械式的思维方式.
优点: 复杂的问题流程化进而简单化
缺点: 可扩展性差
2, 面向对象编程: 核心是对象二字, 对象是技能与特征的结合体, 基于该思想编写程序就好比在创造一个世界, 世界是由一个个对象组成的, 在上帝眼里任何存在的事物都是对象, 任何不存在的事物也都可以创造出来, 是一种上帝式的思维方式
优点: 可扩展性强
缺点: 编程的复杂度要高于面向过程
二, 类与对象
对象: 对象是技能与特征的结合体
类: 对象相同特征与技能的结合体
对象是具体存在的事物, 而类则是抽象出来的概念, 站在不同角度总结出来的类与对象是不同的
在现实世界中, 现有一个个具体存在的对象, 然后随着人类文明的发展才总结出类的概念
# 在现实世界中, 站在老男孩学校的角度: 先有对象, 再有类
对象 1: 李坦克
特征:
学校 = oldboy
姓名 = 李坦克
性别 = 男
年龄 = 18
技能:
学习
吃饭
睡觉
对象 2: 王大炮
特征:
学校 = oldboy
姓名 = 王大炮
性别 = 女
年龄 = 38
技能:
学习
吃饭
睡觉
对象 3: 牛榴弹
特征:
学校 = oldboy
姓名 = 牛榴弹
性别 = 男
年龄 = 78
技能:
学习
吃饭
睡觉
现实中的老男孩学生类
相似的特征:
学校 = oldboy
相似的技能:
学习
吃饭
睡觉
View Code
在程序中, 是先定义出类的概念后调用类来产生对象
- # 在程序中, 务必保证: 先定义(类), 后使用(产生对象)
- PS:
1. 在程序中特征用变量标识, 技能用函数标识
2. 因而类中最常见的无非是: 变量和函数的定义
- # 程序中的类
- class OldboyStudent:
- school='oldboy'
- def learn(self):
- print('is learning')
- def eat(self):
- print('is eating')
- def sleep(self):
- print('is sleeping')
- # 注意:
1. 类中可以有任意 python 代码, 这些代码在类定义阶段便会执行
2. 因而会产生新的名称空间, 用来存放类的变量名与函数名, 可以通过 OldboyStudent.__dict__查看
3. 对于经典类来说我们可以通过该字典操作类名称空间的名字(新式类有限制), 但 python 为我们提供专门的. 语法
4. 点是访问属性的语法, 类中定义的名字, 都是类的属性
# 程序中类的用法
.: 专门用来访问属性, 本质操作的就是__dict__
- OldboyStudent.school #等于经典类的操作 OldboyStudent.__dict__['school']
- OldboyStudent.school='Oldboy' #等于经典类的操作 OldboyStudent.__dict__['school']='Oldboy'
- OldboyStudent.x=1 #等于经典类的操作 OldboyStudent.__dict__['x']=1
- del OldboyStudent.x #等于经典类的操作 OldboyStudent.__dict__.pop('x')
- # 程序中的对象
- # 调用类, 或称为实例化, 得到对象
- s1=OldboyStudent()
- s2=OldboyStudent()
- s3=OldboyStudent()
- # 如此, s1,s2,s3 都一样了, 而这三者除了相似的属性之外还各种不同的属性, 这就用到了__init__
- # 注意: 该方法是在对象产生之后才会执行, 只用来为对象进行初始化操作, 可以有任意代码, 但一定不能有返回值
- class OldboyStudent:
- ......
- def __init__(self,name,age,sex):
- self.name=name
- self.age=age
- self.sex=sex
- ......
- s1=OldboyStudent('李坦克','男',18) #先调用类产生空对象 s1, 然后调用 OldboyStudent.__init__(s1,'李坦克','男',18)
- s2=OldboyStudent('王大炮','女',38)
- s3=OldboyStudent('牛榴弹','男',78)
- # 程序中对象的用法
- # 执行__init__,s1.name='牛榴弹', 很明显也会产生对象的名称空间
- s2.__dict__
- {'name': '王大炮', 'age': '女', 'sex': 38}
- s2.name #s2.__dict__['name']
- s2.name='王三炮' #s2.__dict__['name']='王三炮'
- s2.course='python' #s2.__dict__['course']='python'
- del s2.course #s2.__dict__.pop('course')
- View Code
__init__方法
- # 方式一, 为对象初始化自己独有的特征
- class People:
- country='China'
- x=1
- def run(self):
- print('----->', self)
- # 实例化出三个空对象
- obj1=People()
- obj2=People()
- obj3=People()
- # 为对象定制自己独有的特征
- obj1.name='egon'
- obj1.age=18
- obj1.sex='male'
- obj2.name='lxx'
- obj2.age=38
- obj2.sex='female'
- obj3.name='alex'
- obj3.age=38
- obj3.sex='female'
- # print(obj1.__dict__)
- # print(obj2.__dict__)
- # print(obj3.__dict__)
- # print(People.__dict__)
- # 方式二, 为对象初始化自己独有的特征
- class People:
- country='China'
- x=1
- def run(self):
- print('----->', self)
- # 实例化出三个空对象
- obj1=People()
- obj2=People()
- obj3=People()
- # 为对象定制自己独有的特征
- def chu_shi_hua(obj, x, y, z): #obj=obj1,x='egon',y=18,z='male'
- obj.name = x
- obj.age = y
- obj.sex = z
- chu_shi_hua(obj1,'egon',18,'male')
- chu_shi_hua(obj2,'lxx',38,'female')
- chu_shi_hua(obj3,'alex',38,'female')
- # 方式三, 为对象初始化自己独有的特征
- class People:
- country='China'
- x=1
- def chu_shi_hua(obj, x, y, z): #obj=obj1,x='egon',y=18,z='male'
- obj.name = x
- obj.age = y
- obj.sex = z
- def run(self):
- print('----->', self)
- obj1=People()
- # print(People.chu_shi_hua)
- People.chu_shi_hua(obj1,'egon',18,'male')
- obj2=People()
- People.chu_shi_hua(obj2,'lxx',38,'female')
- obj3=People()
- People.chu_shi_hua(obj3,'alex',38,'female')
- # 方式四, 为对象初始化自己独有的特征
- class People:
- country='China'
- x=1
- def __init__(obj, x, y, z): #obj=obj1,x='egon',y=18,z='male'
- obj.name = x
- obj.age = y
- obj.sex = z
- def run(self):
- print('----->', self)
- obj1=People('egon',18,'male') #People.__init__(obj1,'egon',18,'male')
- obj2=People('lxx',38,'female') #People.__init__(obj2,'lxx',38,'female')
- obj3=People('alex',38,'female') #People.__init__(obj3,'alex',38,'female')
- # __init__方法
- # 强调:
- # 1, 该方法内可以有任意的 python 代码
- # 2, 一定不能有返回值
- class People:
- country='China'
- x=1
- def __init__(obj, name, age, sex): #obj=obj1,x='egon',y=18,z='male'
- # if type(name) is not str:
- # raise TypeError('名字必须是字符串类型')
- obj.name = name
- obj.age = age
- obj.sex = sex
- def run(self):
- print('----->', self)
- # obj1=People('egon',18,'male')
- obj1=People(3537,18,'male')
- # print(obj1.run)
- # obj1.run() #People.run(obj1)
- # print(People.run)
- View Code
类是一系列对象相同特征与技能的结合体, 既类体中最常见的就是变量与函数体, 但其实类体中是可以存在任意 python 代码的,
类体中会在定义阶段立即执行, 会产生一个类的名称空间, 用来将类体代码执行过程中产生的名字都丢进去
总结:类本质就是一个名称空间, 或者说就是一个用来存放变量与函数的容器,
类的用途之一就是当做名称空间, 从其内部取出名字来使用
类的用途之二是用来调用类产生对象
调用类产生对象: 调用类的过程称之为类的实例化, 调用类的返回值称之为类的一个对象
View Code
调用类发生了:
1, 产生一个空对象
2, 触发类中的__init__方法, 将对象连同调用类括号内指定的参数一同传入__init__
类中定义的变量是类的数据属性, 类可以用, 对象也可以用, 大家都指向同一个地址, 类变量值一旦改变, 所有对象都跟着改变
类中定义的函数是类的函数属性, 类可以用, 类来调用就是一个普通的函数, 但其实类中定义的函数就是给对象用的, 而且是绑定给对象用的
绑定方法: 指向类的函数(特殊之处是绑定给谁就应该由谁调用)
类的函数: 该传几个参数就传几个参数
三大特性之继承:
利用继承能来解决类与类之间的代码冗余问题
继承是一种新建类的方法, 新建的类称之为子类(派生类), 被继承的类称之为父类, 基类, 超类
继承的特性: 子类可以遗传, 重用父类的属性
python 中继承类的特点:
1, 在 python 中一个子类可以同时继承多个父类
2, 在继承背景下说, python 中类分为两种: 新式类和经典类
新式类: 但凡继承了 object 的类以及该类的子类都是新式类
python 中一个类即便没有显示的继承任何类, 默认就会继承 object, 即在 python3 中所有的类都是新式类
经典类: 没有继承 object 的类以及该类的子类都是经典类
在单继承的基础下属性查找的顺序是: 对象 --对象类 --父亲 --父类...
子类中重用父类的属性:
方式一:
在子类派生出的新方法中指名道姓的引用某一个类中的函数
与继承无关, 访问的是父类中的函数没有自动传值功能
继承解决的是类与类之间代码冗余的问题, 一定是一个类是另外一个类的子类
总结对象之间的相似之处得到类, 总结类之间的相似之处得到的就是父类
多继承背景下属性查找的顺序: 对象 --对象的类 --按照从左往右的顺序一个个的分支找下去
一旦出现菱形继承问题, 新式类与经典类在属性查找上的区别是:
新式类: 广度优先查找, 在最后一个分支查找顶级类
经典类: 深度优先查找, 在第一个分支就查找顶级类
mro()查找的顺序:
在子类派生出的新的方法中重用父类功能的方式二
在子类中用 super()方法
python2 中: super(自己的类名, 对象自己)
python3 中: super()
调用 super()方法会得到一个特殊的对象, 该对象是专门用来引用父类中的属性, 完全参照 mro()列表 访问是绑定方法, 有自动传值的效果
组合:
组合指的是一个对象拥有一个属性, 该属性的值属于另外一个类的对象
组合通过一个对象添加属性 (属性的值是另外一个类的对象) 的方式, 可以间接的将两个类关联, 整合, 从而减少类之间的代码冗余问题
多态:
多态指的是同一事物的不同形态
在多态背景下, 可以不用考虑对象具体的类型的前提下直接使用对象多态的精髓: 统一
父类只是用来建立规范的, 不能用来实例化的, 更无需实现内部方法.
python 崇尚鸭子类型
封装:
装: 往容器, 名称空间内存入名字
封: 代表存放于名称空间的名字给藏起来, 这种隐藏对外不对内
在类内定义的属性前加__开头(没有__结尾)
总结:
1,__开头的属性实现的隐藏仅仅只是一种语法上的变形, 并不会真正的限制类外部的访问
2, 该变形操作只在类定义阶段检测语法时发生一次, 类定义阶段之后新增的__开头的属性不会变形
3, 如果父类不想让子类覆盖自己的属性, 可以在属性前加__开头
4,peoperty 装饰器是用来将类内的函数属性伪装为数据属性
绑定方法与非绑定方法
一: 绑定方法(绑定给谁, 谁来调用就自动将它本身当作第一个参数传入):
1. 绑定到类的方法: 用 classmethod 装饰器装饰的方法.
为类量身定制
类. boud_method(), 自动将类当作第一个参数传入
(其实对象也可调用, 但仍将类当作第一个参数传入)
2. 绑定到对象的方法: 没有被任何装饰器装饰的方法.
为对象量身定制
对象. boud_method(), 自动将对象当作第一个参数传入
- (属于类的函数, 类可以调用, 但是必须按照函数的规则来, 没有自动传值那么一说)
- import settings
- class MySQL:
- def __init__(self,host,port):
- self.host=host
- self.port=port
- @classmethod
- def from_conf(cls):
- print(cls)
- return cls(settings.HOST,settings.PORT)
- print(MySQL.from_conf) #<bound method MySQL.from_conf of <class '__main__.MySQL'>>
- conn=MySQL.from_conf()
- conn.from_conf() #对象也可以调用, 但是默认传的第一个参数仍然是类
二: 非绑定方法: 用 staticmethod 装饰器装饰的方法
1. 不与类或对象绑定, 类和对象都可以调用, 但是没有自动传值那么一说. 就是一个普通工具而已
注意: 与绑定到对象方法区分开, 在类中直接定义的函数, 没有被任何装饰器装饰的, 都是绑定到对象的方法, 可不是普通函数, 对象调用该方法会自动传值, 而 staticmethod 装饰的方法, 不管谁来调用, 都没有自动传值一说
- import hashlib
- import time
- class MySQL:
- def __init__(self,host,port):
- self.id=self.create_id()
- self.host=host
- self.port=port
- @staticmethod
- def create_id(): #就是一个普通工具
- m=hashlib.md5(str(time.time()).encode('utf-8'))
- return m.hexdigest()
- print(MySQL.create_id) #<function MySQL.create_id at 0x0000000001E6B9D8> #查看结果为普通函数
- conn=MySQL('127.0.0.1',3306)
- print(conn.create_id) #<function MySQL.create_id at 0x00000000026FB9D8> #查看结果为普通函数
- View Code
classmethod 与 staticmethod 的区别
- import settings
- class MySQL:
- def __init__(self,host,port):
- self.host=host
- self.port=port
- @staticmethod
- def from_conf():
- return MySQL(settings.HOST,settings.PORT)
- # @classmethod #哪个类来调用, 就将哪个类当做第一个参数传入
- # def from_conf(cls):
- # return cls(settings.HOST,settings.PORT)
- def __str__(self):
- return '就不告诉你'
- class Mariadb(MySQL):
- def __str__(self):
- return '<%s:%s>' %(self.host,self.port)
- m=Mariadb.from_conf()
- print(m) #我们的意图是想触发 Mariadb.__str__, 但是结果触发了 MySQL.__str__的执行, 打印就不告诉你:
- View Code
三, 元类
元类源自一句话, 在 python 中一切皆对象, 而对象都是由类实例化得到的, 既然这样那样类也是对象, 也是通过实例化得到的, 内置类为 type
调用关系是:
调用元类 --自定义的类
调用自定义的类 --自定义的对象
自定义类的三个关键组成部分
1 类名
2 类的基类们
3 类的名称空间
class 关键字的底层的工作原理
1, 先拿到类名(oldboyteacher)
2, 再拿到类的基类们:(object,)
3, 拿到类的名称空间(执行类体代码, 将产生的名字放到类的名称空间也就是一个字典里, 补充 exec)
4, 调用元类实例化得到自定义的类:,oldboyTeacher=type('oldboyTeacher',(object){})
实现代码
- class_name="OldboyTeacher"
- class_bases=(object,)
- class_dic={}
- class_body='''
- school=self.shcool
- def __init__(self,...)
- .............
- def score(......)
- ...........
- exec('x=1',{},class_dic)
- print(class_dic)
- OldboyTeacher_type(class_name,class_bases,class_dic)
- View Code
自定义元类来控制类的产生:
但凡继承了 type 的类才能称之为自定义的元类, 否则就是一个普通的类
对象之所以可以调用是因为对象有一个函数__call__
实例化发生的三件事:
1, 产生一个空对象
2, 执行__init__方法, 完成对象初始化属性的操作
3, 返回初始化好的那个对象
自定义类来控制类的调用
来源: https://www.cnblogs.com/chenchuanjide/p/9512446.html