面向对象 OOP
- 面向对象是一种认识与分析世界的方法论
- 将万事万物抽象为类
- 类 class
- 一类事物公共特征的集合
- 用计算机语言描述就是属性与方法的集合
- 对象 object / 实例 instance : 类的具体个体
- 属性 : 对象状态的抽象
- 操作/方法 : 对象行为的抽象,抽象为函数
- 面向对象三要素 : 封装 继承 多态
- 封装
- 将数据和操作组合在一起
- 隐藏数据,对外只提供接口 类似黑箱
- 继承
- 多复用,继承父类的属性与方法
- 多继承少修改,使用继承来改变,体现个性
- 多态
- 面向对象最灵活的地方,动态绑定
- 封装
python类
- 使用class关键字
- 类名使用大驼峰 形如ClassName
- 类定义完后,产生一个类对象,绑定在标识符ClassName上
class Human:
NAME='human name'
def __init__(self,name,age):
self.name=name
self.age=age
def eat(self):
print(self.name,' is eating')
t=Human('tom', 20)
t.eat()
print(t.age,t.NAME)
实例化
- t=Human(‘tom’, 20) 就是实例化
- 每次实例化得到不同的实例
- 即使实例化时使用同样的参数,也得到不同的实例
- Python实例化,会调用 __init__(self)方法
- 未定义__init__(self)则会在实例化时隐式调用
- 作用 : 对实例进行初始化
- __init__(self)的第一个参数self表示实例本身 (名称self是一种惯例)
- 还有其他参数需要在实例化时填入
实例对象
- 实例化后生成的对象称为实例对象
- 实例对象绑定方法
- 以上面的实例对象t为例
- 调用方法时采用t.eat()的方式
- 函数签名为eat(self)
- self.name是实例的属性 保存在具体实例中,而不是保存在Human类.
- 形如 self.name 的称为实例变量,为实例独有
- 定义在类上的变量为类变量,所有实例共享
- 一般来说,类变量使用全大写命名
特殊属性
- 对象的特殊属性形如__attribute__
- 是python里的magic
特殊属性 | 含义 | 实例对象 | 类对象 |
---|---|---|---|
__name__ | 对象名 | 报错 | 类名 |
__class__ | 对象的类型 | main.类名 | type |
__dict__ | 对象的属性字典 | 实例属性字典 | 类属性字典 |
__qualname__ | 类的限定名 | 报错 | 类名 |
- 类与实例的属性分别保存于对应的__dict__属性中
- 若要从实例访问类属性,可借助__class__找到所属的类
实例属性(实例变量)与类属性(类变量)优先级
- 类变量是属于类的变量,所有实例亦可共享
- 实例变量只有自身实例可以访问 其他实例与类都不可访问
- 实例属性的查找顺序
- 实例通过.访问属性
- 先去自身实例查找对应属性(实例的__dict__),找到就返回
- 找不到再去类里(类的__dict__)找同名属性,找到则返回
- 还找不到则报错,没有对应属性
- 若实例使用__dict__[变量名]访问变量,则不会查找所在类的字典
装饰类 : 增加类属性
- 装饰器也可以装饰类
- 要求 : 在Human类中增加NAME属性
- 函数版本 :
class Human:
AGE=8
print(Human.__dict__)
def addname(name,cls):
c.NAME=name
return cls
addname('tom',Human)
print(Human.__dict__)
- 装饰器版本 :
def addname(name):
def wrapper(cls):
cls.NAME=name
return cls
return wrapper
@addname('tom') # Human=addname('tom')(Human)
class Human:
AGE=8
print(Human.__dict__)
类方法与静态方法
- 类方法
- 在类定义中,使用@classmethod装饰器修饰的方法
- 必须至少有一个参数,第一参数常命名为cls cls指代调用者,即类对象本身
- 首个参数命名为cls是一种约定 为了代码易读性请勿修改
- 通过cls可直接操作类的属性
- 类方法类似于C++ Java中的静态方法
class Human:
@classmethod
def class_method(cls):
print('class={0.__name__}({0})'.format(cls))
cls.HEIGHT=170
Human.class_method()
Human.__dict__
- 静态方法
- 在类定义中,使用@staticmethod装饰器修饰的方法
- 调用时,不会隐式地传入参数
class Human:
@classmethod
def class_method(cls):
print('class={0.__name__}({0})'.format(cls))
cls.HEIGHT=180
@staticmethod
def static_method():
print(Human.HEIGHT)
- 类调用普通方法会报错,
- 因为普通方法第一参数需要传入实例本身
- 类是抽象概念,无实例
私有(Private)属性
- 属性名使用双下划线开头
- 本质 : Python解释器将__变量名 改名为 _类名__变量名
- 保护变量 : 变量名前加一个下划线
- 保护变量与普通变量一样,解释器未做改名处理
- 是开发者的共同约定,看做私有变量
- 私有方法 同上
函数名之前加下划线或双下划线
私有方法的本质
- 单下划线的方法只是开发者之间的约定,解释器不做任何改变
- 双下划线的方法为私有方法.解释器会将__方法名 改名为 _类名__方法名
- 私有方法在类的__dict__属性中可以找到
- Python中没有绝对的私有属性
- 只是双下划线开头的属性被改了名
- 前导下划线只是一种警告与提醒,请遵守这一约定
- 除非真有必要,否则请勿使用私有成员,更不要修改他们
property装饰器
- 为防止属性被外部直接访问甚至修
- 可定义函数用于获得以及修改属性
- property装饰器可将函数装饰成属性
- 同时定义该’属性’是否可变
- 有装饰器与函数两种用法
- 装饰器用法 :
import random
class RandInt:
def __init__(self,count=3):
self.__len=count
def makenum(self,lrange=1,rrange=50):
return [(random.randint(lrange,rrange)) for _ in range(self.__len)]
@property
def leng(self):
return self.__len
@leng.setter
def leng(self,count):
self.__len=count
- 函数调用法 :
import random
class RandInt:
def __init__(self,count=3):
self.__len=count
def makenum(self,lrange=1,rrange=50):
return [(random.randint(lrange,rrange)) for _ in range(self.__len)]
fget=lambda x:x.__len
def fset(self,count):
self.__len=count
leng=property(fget, fset)
- 以上两种代码都可将leng函数装饰为属性,实例可直接访问leng属性
- 同时再加入@leng.setter装饰器(或在property函数的第二个参数传入fset函数)可直接通过赋值语句修改属性
- fset函数可实现更严格的判断,条件具备才能更改属性值 可避免直接修改属性值