面向对象
面向对象
一种认识世界,分析世界的方法论.将万事万物抽象为类.
面向过程
流水线,一步一步走
优点:逻辑清晰
缺点:上一步错了,下一步就一起错
类class
是抽象概念,一类事物的共同特征的集合
定义时候发生的事
对象instance,object
对象是具体的,一个实体
用就按及语言来描述类,就是属性和方法的集合.
属性,是对对象状态的抽象,用数据结构来描述
方法,是对对象行为的抽象,用操作名和实现该操作的方法来描述
类内部的函数就是方法
思想
Python中,一切皆对象
对象是对数据和操作的封装
对象是独立的,但是对象之间可以相互使用
目前OOP(Object Oriented Programming)是最接近人类认知的编程范式
面向对象三要素
1.封装
- 组装:将数据和操作组装到一起
- 隐藏数据:对外只暴露一些接口,通过接口访问对象.
2.继承
- 多重复,继承来的就不用自己写
- 多继承少修改,OCP(Open-closed Principle),使用继承来改变,来体现个性
3.多态
- 面向对象编程最灵活的地方,动态绑定
- 多态也称多态性
- 目的:统一子类编写规范,为了让使用者更加方便调用功能的方法.
实例化
class MyClass:
x = 123 #类属性
def __init__(self):
print('init')
def foo(self):
return self.x
a = MyClass() #实例化
>>>init
使用上述语法,在类对象名称后加一个括号,就调用类的实例化方法,完成实例化.
实例化就真正创建了一个该类的对象(实例),同时产生对象的名字空间(字典)
__init__
初始化方法
作用:对实例进行初始化(只做对实例的属性进行初始化)
Python类实例化后,会自动调用__init__方法.初始化可以多个参数,但是第一个参数必须留给self,其他参数随意.
MyClass()实际上就是在调用__init__方法,可以不定义,如果没有定义会在实例化后隐式调用
__init__方法不能有返回值,只能是None.(约定)
Python之禅说,显式比隐式更好,所以,应该使用__init__
通过类方法,即可以对类的属性进行修改,也可以对实例的属性进行修改.
实例对象instance(object)
类实例化后一定会获得一个对象,这就是实例对象.
self.name 就是对jerry对象的name,name保存在对象jerry对象上,而不是Person类上,所以叫实例变量.name称为实例的属性
self
self就是调用者,就是对应的实例对象.
实例变量 和 类变量
类的属性可在类内部或者外部更改,更改后,后续定义的类的属性也会被更改
age = 3 是类变量
name 是实例变量
- 实例变量是每一个实例自己的变量;
- 类变量是类的变量,是类的所有实例共享的属性和方法
特殊属性 | 含义 |
---|---|
__name__ | 类的对象名(实例没有) |
__class__ | 对象的类型(同type) |
__dict__ | 对象的属性字典 |
__qualname__ | 类的限定名(实例没有) |
总结
是类的,也是这个类所有实例的,其实例都可以访问到;是实例的,就是这个实例自己的,通过类访问不到.
类变量是书屋类的变量,这个类的所有实例可以共享这个变量.
实例可以动态的给自己增加一个属性.比如:__dict__[变量名]和实例.变量名都可以访问到.
实例的同名变量会隐藏这些类变量,或者说覆盖了这个类变量.
- 实例属性的查找顺序
- 指的是实例使用 . 来访问属性,会首先找自己的__dict__,给如果没有,在通过__class__找到自己的类,再去类的__dicr__中找
- 如果实例使用__dict__[变量名]访问变量,将不会按照上面的查找顺序找变量,这个是指明使用字典的key查找,不是属性查找
- 一般来说,==类变量使用全大写来命名.
类方法和实例方法
类方法
@classmethod
def clsmtd(cls):
print(xxx)
a = MyClass()
a.clsmtd()是可以调用的, 等同于a.__class__.clsmtd()
- 在类定义中,使用@classmethod装饰器修饰的方法
- 必须至少有一个参数,且第一个参数就给了cls,cls指调用者就是对象自己
- cls可以是任意合法名称,但是为了易读性,不要改
- 通过cls可以直接操作类的属性,但是无法通过cls操作类的实例
静态方法
@staticmethod
def staticmtd():
print('static')
- 在类的定义中,使用@staticmethod装饰器修饰的方法
- 调用时,不会隐式传入参数
静态方法,指时表明这个方法属于这个名词空间,函数归结在一起,方便组织管理.
总结
类除了普通方法都可以调用,普通 方法 需要对象的实例作为第一参数.
实例可以调用所有类中定义的方法(包括类方法,静态方法),普通方法传入实例自身,静态方法和类方法需要找到实例的类.
访问控制
私有(private)属性
双下划线开头
实例.__方法 外部不可使用此方法;在内部定义一个获取此方法的方法,才可以使用
外部无法访问,但是类内部接口可以访问
class Person:
def __init__(self,name,age=18):
self.name= name
self.__age= age
def growup(self,increase = 1):
if 0< increase < 150:
self.__age += increase
def getage(self):
return self.__age
此时age在外部不可见,但是可以通过调用getage来访问
本质:python通过内部改名来实现,将其改名为_类名__方法名
__age
存在于实例的属性字典中
如果__growup
属于私有方法,存放在类的属性字典中
保护变量(protected)
单下划线开头
开发者约定 ,单下划线不要改
公共方法(public)
私有成员总结
python使用中,使用单下划线或者双下划线来表示一个成员被保护或者被私有化隐藏起来.不要使用或者修改
补丁
通过修改或者替换类的成员,使用者调用的方式没有改变,但是,类提供的功能可能已经改变了
猴子补丁(monkey patch)
在运行时,对其属性进行动态替换(慎用)
属性装饰器
一般好的设计:把实例的属性保护起来,不让外部直接访问,外部使用getter读取属性和setter方法设置属性.
-
property装饰器
后面跟函数名就是以后的属性名.它就是getter,这个必须有,有了它至少是只读属性
将类内部的方法def()变成def,
-
setter装饰器
与属性同名,接收两个参数,第一个是self,第二是将要赴的值.有了它,属性可写
-
deleter装饰器
可以控制是否删除属性,很少用
-
property装饰器必须在前,setter,deleter装饰器在后
多态
实现:
- 继承
- 抽象类(abc模块)abc模块强制子类按照父类内属性定义,也可派生新的属性和方法
鸭子类型
使用代码外部规范来定义代码形式
-继承:耦合性太高,程序的可扩展性差
-鸭子类型:耦合度低,程序的可扩展性强
对象的销毁
类中可以定义__del__
方法,称为析构函数(方法).
作用:销毁类的实例的调用,以释放专用内存
方法重载(overload)
重载就是同一个方法名,但是参数数量.类型不一样,就是同一个方法的重载.
Python没有重载,因为Python支持可变形参,参数的数据类型也无法固定,所以没必要.
封装
面向对象的三要素之一,封装Encapsulation
封装
将数据和操作组织到类中,及属性和方法.
将数据隐藏起来,给使用者提供操作.是用者通过操作就可以获取或者修改数据,getter ,setter
通过访问控制,暴露适当的数据和操作给用户.
方法绑定(bound method)
类内部的方法主要是给对象用的
1.类调用类内部的函数,该函数只是一个普通的函数.
普通函数需要接收几个参数就要传入几个参数.
2.对象调用类内部方法:称之为方法绑定
不同的对象调用类内部方法,会产生不同的对象的方法绑定,对象的绑定方法,是由对象来调用.但其方法功能都是一致的.
class Student:
school = 'oldboy'
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex,
def learn(self):
pass
def eat(self):
pass
stu1 = Student('james',15,'male')
stu2 = Student('james',15,'male')
print(stu1.__dict__)
print(Student.learn)
print(stu1.learn)
print(stu2.learn)
>>>{'name': 'james', 'age': 15, 'sex': ('male',)}
>>><function Student.learn at 0x0000021246682598>
>>><bound method Student.learn of <__main__.Student object at 0x000002125D67C710>>
>>><bound method Student.learn of <__main__.Student object at 0x000002125D67C550>>
---------------------------------------------------------------
-
类名必须使用驼峰体来命名
-
在定义类发生的事情:
- 生成一个名称空间(类的字典)
- 把类中所有名字,放入类的字典中
- 使用
__dict__
方法查看
注意:类在定义阶段就已经产生好了名称空间,执行Python文件时会执行类内部的代码.
-
调用类发生的事情:
- 首先产生一个空的对象,产生名称空间
- 自动触发
__init__
- 把对象本身以及括号内的参数一并传给
__init__
函数
-
对象与类的查找顺序:
- 对象 . 属性,若对象本身有,则优先查找对象自己的
- 若对象本身没有,则取类里面找
- 若类没有,报错