| 一 面向对象基本理论 |
面向过程:核心是过程,过程就是解决问题的步骤,即先干什么,再干什么
基于面向过程设计程序,就好比在设计一条流水线,是一种机械思维方法
优点:复杂的问题简单化
缺点:可扩展性差(牵一发而动全身)
应用场景:扩展性低的场合,如linux内核,httpd,git
面向对象:核心是对象,要理解对象应该把自己当成上帝,在上帝眼中一切存在的事物都是对象,不存在也可以创建出来
优点:可扩展性强
缺点:无法像面向过程一样准确地知道什么阶段发生什么事,会有什么结果
应用场景:与用户交互多的,公司内部的软件,游戏,互联网软件
特征与技能的结合体就是一个对象,一系列对象共有的特征(变量的定义)与技能(函数的定义)的结合体就是类
在Python中,用变量表示特征,用函数表示技能,因而类是变量与函数的结合体,对象是变量与方法(指向类的函数)的结合体
类的定义:类是一系列对象共有的特征(变量的定义)与技能(函数的定义)的结合体
PS:在Python3中统一了类型与类的概念
定义类语法:
class 类名:
'''注释'''
类体(可以是任意代码,在定义阶段就执行,产生了作用域的概念)
类的使用:
1. 属性的引用,定义类会执行,生成名称空间
- 数据属性:Chinese.country
- 函数属性:Chinese.talk('123') # 需要几个参数,就得传几个参数,如self
2. 实例化,类是抽象的,实例化后变为对象
- p = Chinese()
对象的使用:只有一种,就是属性引用
- p.country
- p.talk() # 会把p当为一个参数传入talk(self)
对象可以有自己的特征值
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
类实例化本质
p1 = Chinese('Linda', 18, 'female') # Chinese.__init__(p1, 'Linda', 18, 'female')
self=p1, p1.name=name, p1.age=age, p1.sex=sex
类名称空间
- print(Chinese.__dict__) # 属性字典
对象名称空间
- print(p1.__dict__) # 属性字典
print(p1.name) # 等价于 p1.__dict__['name'],本质以变量名的字符串作为key,去调用__dict__,取得值
print(p1.__dict__['name'])
对象使用属性时,先在自己对象的名称空间,再找所属类的名称空间
小结:
1. 类具有数据属性和函数属性,而对象只有数据属性;但是对象可以调用类的方法
- Chinese.talk(123) # 类调用时,和普通方法一样,需几个参数,就传几个参数
- p1.talk() # 绑定方法,绑定的含义就是p1默认作为一个参数传入,Chinese.talk(p1)
谁来用,就是谁的效果,p1.talk()就是p1的效果,self,__main__.Chinese object
2. 定义在类内部的变量,是所有对象共有的,id全一样
3. 定义在类内部的函数,是绑定到所有对象的,是给对象来用,obj.func()会把obj本身当做一个参数
| 二 封装、继承、多态 |
什么是继承?
继承是一种创建新类的方式,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类
class ParentClass(object):
pass
class SubClass(ParentClass):
pass
print(SubClass.__bases__)
抽象即抽取类似或者说比较像的部分
抽象分成两个层次:
1. 将奥巴马和梅西这俩抽象比较像的部分抽取成类;
2. 将人、猪、狗这三个类比较像的部分抽取成父类。
抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)
继承:是基于抽象的结果,通过编程语言去实现它,肯定先经历抽象这个过程,
才能通过继承的方式去表达抽象的结构。
继承的好处一:减少冗余代码
class People(Animal):
def __init__(self, name, age, sex, education):
Animal.__init__(self, name, age, sex)
self.education = education
在子类定义新的属性,覆盖掉父类的属性,称为覆盖,override
s.bar(), 顺序:对象自己的__dict__,类的__dict__,父类的__dict__,....
继承反映的是 一种什么是什么的关系
组合反映的是 一种什么有什么的关系
继承的实现原理 - Python3
''' E D A B C F Python3 广度优先:F - A - E - B - D - C '''
在子类调用父类的方法
super(自己类名, self).__init__() # 父类的__init__()
使用super()调用的所有属性,都是从MRO列表当前的位置往后找。
多态
同一种事物的多种形态,指的是父类
people_obj1.talk() pig_obj1.talk() dog_obj1.talk() def func(obj): obj.talk()
好处:同一种调用方式,传入不同的对象,实现不同功能
封装不是单纯的隐藏
封装数据的主要原因:保护隐私
封装方法的主要原因:隔离复杂度
封装其实分为两个层面,但无论那种层面的封装,都要对外界提供访问你内部隐藏内容的接口
第一层面的封装(什么都不用做):创建类和对象分别创建二者的名称空间,我们只能用类名.或者obj.的方式访问里面的名字,这本身就是一种封装
第二个层面的封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只能在类的内部使用、外部无法访问,或者留下少量(函数)供外部访问
在Python中用双下划线的方式实现隐藏属性(设置成私有的)
语法层面,定义__x时,自动变为_People__x,这种自动变形的特点:
1. 类中定义的__x只能在内部使用,如self.__x,引用的是变形后的结果
2. 这种变形其实是针对外部的变形,在外部是无法通过__x这个名字访问到的
3. 在子类定义的__x不会覆盖父类定义的__x
property 作为类函数的装饰器,把类的函数属性,变为数据属性
class Foo(object): @property def test(self): print('from Foo.test') obj = Foo() obj.test
也是一种封装,当test为名词时,更适合作为属性,而不是方法
被@property装饰的函数,是不能直接赋值的,得通过@test.setter定义单独函数来设置
class People(object): def __init__(self, name): self.__name =name @property def name(self): return self.__name @name.setter def name(self,value): self.__name = value @name.deleter def name(self): del self.__name p1 = People('Linda') print(p1.name) p1.name = 'Alex' print(p1.name) del p1.name print(p1.name) # 报错
绑定方法与非绑定方法
在类内部定义的,只要没有装饰器,就是绑定到对象的方法,给对象用的,对象默认作为第一个参数传入
在类内部定义的,有@classmethod装饰器,是绑定到类的方法,给类用的,类默认作为第一个参数传入
class Foo(object): @classmethod def test(cls): print(cls) f = Foo() Foo.test() # 类作为第一个参数传进去,结果为:<class '__main__.Foo'> f.test() # 即使是对象调用,也是把所属类传进去,结果为:<class '__main__.Foo'>
在类的定义定义的,有@staticmethod装饰器,就不是绑定方法了,不存在默认传值的问题,定义几个参数,就传几个参数即可
class Foo(object): @staticmethod def test(): print('from Foo.test') Foo.test() # from Foo.test f = Foo() f.test() # from Foo.test
分析小结:
class Foo(object): def test1(self): pass @classmethod def test2(cls): pass @staticmethod def test3(): pass f = Foo() print(f.test1) # <bound method Foo.test1 of <__main__.Foo object at 0x000000D539EB55F8>> print(Foo.test2) # <bound method Foo.test2 of <class '__main__.Foo'>> print(f.test3) # <function Foo.test3 at 0x000000D539EA6D08> print(Foo.test3) # <function Foo.test3 at 0x000000D539EA6D08>
小例子:
import settings import hashlib import time class MySQL(object): def __init__(self, host, port): self.id = self.create_id() # create_id()具有生成唯一id的功能,普通工具包而已 self.host = host self.port = port print('connecting...') @classmethod def from_conf(cls): return cls(settings.HOST, settings.PORT) # MySQL('127.0.0.1', 3306),cls功能:实例化,得到对象 @staticmethod # 非绑定方法,普通工具包,不需传入self或者cls def create_id(): m = hashlib.md5(str(time.clock()).encode('utf-8')) return m.hexdigest() def select(self): print('select...') conn1 = MySQL('192.168.0.1', 3306) # 第一种初始化,用户输入 conn2 = MySQL.from_conf() # 第二种初始化,从配置文件读取
| 面向对象高级部分 |
1. isinstance(obj, cls)与issubclass(sub, super)
isinstance(obj, cls)检查obj是否是类cls的对象
issubclass(sub, super)检查sub是否是类super的子类
2. 反射
反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)
Python面向对象的反射:通过字符串的形式操作对象相关属性。Python中一切事物都是对象(都可以使用发射)
四个可以实现自省的函数,下列方法适用于类和对象
# 判断object中有没有一个name字符串对应的方法或属性