面向对象和面向过程
面向过程的编程思想
面向过程的核心就是过程二字,过程值得就是解决问题的步骤,即先干什么,再干什么,后干什么;基于该思想的编写程序就好比在设计一条流水线,是一种机械式的思维方式。
优点是复杂的问题流程化,进而简单化,缺点是程序的可扩展性较差
面向对象的编程思想
面向对象的核心就是对象二字,对象是特征与技能的结合体,基于该思想编写的程序就好比在创建一个世界,世界是有一个个对象组成的,在上帝的眼里任何存在的事物都是对象,任何不存在的事物也都可以造出来,是一种上帝式的思维方式
优点是程序的可扩展性较强,缺点是编程的复杂度要高于面向过程
类:
对象是特征与技能的结合体,而类就是一系列对象相同的特征与技能的结合体,在这里需要强调的两点就是:对象是具体存在的事物,而类则是一个抽象的概念;站在不同的角度总结出来的类和对象可以是不同的。
在现实的世界中,先有一个个具体存在的对象,然后随着人类文明的发展才总结出来的概念;但是在程序中,一定是先定义类,在后面调用类来产生对象。
# 先定义类
class Student:
# 相同的特征
schoolname = 'XX school'
# 相同的技能
def choose_course(self):
print('正在选课中····')
类体中最常见的就是变量与函数的定义,但其实类体中是可以存在任意python代码的
类体代码会在类定义阶段立即执行,会产生一个类名称空间,用来将类体代码执行过程中产生的名字都丢进去,可以通过 __ dict __查看方式如下
查看类的名称空间:
print(Student.__dict__)
查看类的属性:
print(Student.schoolname) 相当于→print(Student.__dict__['schoolname'])
可以对类的属性进行修改,增加,删除等操作:
Student.school='YY school' 相当于→S tudent.__dict__['school']='YY school'
Student.country='China' 相当于→ Student.__dict__['country']='China'
del Student.country 相当于→ del Student.__dict__['country']
小结:
- 类的本质就是一个名称空间,或者说是一个用来存放变量与函数的容器。
- 类的一个用途就是当作名称空间从其内部取出名字来使用。
- 类的第二个用途就是通过调用类产生对象。
对象
调用类的过程称之为类的实例化,调用类的返回值称之为类的一个对象/实例。
调用类发生了什么:
1.先产生一个空对象,然后返回
2.触发类中的函数 __ init __ 的执行,将对象连同调用类括号nei’zhi’d
class Student:
#相同的特征
school = 'xx学校'
def __init__(obj, name, age, sex):
obj.name = name
obj.age = age
obj.sex = sex
#相同的技能
def choose_course(self):
print('choosing course')
stu1 = Student('peter',20,'male') 相当于→__init__(stu1,'李铁蛋',18,'male')
print(stu1.__dict__)
'''
输出:{'name': 'peter', 'age': 20, 'sex': 'male'}
'''
总结 __ init __ 的功能: 是在实例化时就为对象初始自己独有的特征
注意:不能有返回值
对象的属性查找顺序:先从对象自己的名称空间找,没有则去所属的类中找。
类中定义的变量是所有对象共享的,对象可以来用,类也可以来使用,大家都指向同一个内存地址,类变量值一旦改变所有对象都跟着变
类中定义的函数是类的函数属性,类可以用,类来调用就是一个普通的函数,但其实类中定义的函数是给对象用的,而且是绑定给对象用的。
类的函数: 该传几个参数就传几个。
绑定方法,指向类的函数: 特殊之处是绑定给谁就应该由谁来调用,谁来调用就会将谁当做第一个参数自动传入
面向对象的三大特性:封装、继承、多态
继承
继承是一种新建类的方式,新建的类称之为子类或派生类,被继承的类称之为父类、基类或超类
Python 中继承的特点:
- 子类可以遗传或重用父类的属性
- Python中一个子类可以同时继承多个父类
- 在继承背景下说,python中的类可以分为形式类和 经典类
继承的语法:
class People:
school = 'xx学校'
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
class Student(People):
def choose_course(self):
print('%s choosing course' % self.name)
class Teacher(People):
def score(self,stu,num):
stu.score=num
tea1=Teacher('peter',20,'male')
print(tea1.__dict__)
print(tea1.school)
'''
输出:
{'name': 'peter', 'age': 20, 'sex': 'male'}
xx学校
'''
在单继承背景下属性的查找优先级:对象->对象的类->父类->父类…
在多继承背景下属性的查找优先级:
如果一个子类继承多个分支(多个分支没有共同继承一个非object的类),此时属性的查找优先级是:对象->对象的类->按照从左往右的顺序一个分支一个分支的找下去
菱形继承问题:
新式类 : 广度优先查找,从左往右一个分支一个分支的查找,在最后一个分支才去查找顶级类
经典类 : 深度优先查找,从左往右一个分支一个分支的查找,在第一个分支就查找顶级类
# 第四层:
class G(object):
# x = 'G'
pass
# 第三层
class E(G):
# x = 'E'
pass
class F(G):
# x = 'F'
pass
# 第二层
class B(E):
# x = 'B'
pass
class C(F):
# x = 'C'
pass
class D(G):
# x = 'D'
pass
# 第一层
class A(B, C, D):
# x = 'A'
pass
obj=A()
# obj.x=111
print(obj.x)
新式类(广度优先): obj->A->B->E->C-F->D->G->object
经典类(深度优先): obj->A->B->E->G->C-F->D
在子类派生出的新方法中重用父类功能的方式一:
指名道姓地引用某一个类中的函数
总结:
- 与继承无关
- 访问是类的函数,没有自动传值的效果
class People:
school = 'xx学校'
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
class Student(People):
def __init__(self, name, age, sex, score=0):
People.__init__(self,name,age,sex)
self.score = score
在子类派生出的新方法中重用父类功能的方式二:super()必须在类中用
在python2中:super(自己的类名,自己的对象)
在python3中:super()
调用该函数会得到一个特殊的对象,该对象专门用来访问父类中的属性,完全参照mro列表
总结:
- 严格依赖继承的mro列表
- 访问是绑定方法,有自动传值的效果
class People:
school = 'xx学校'
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
class Student(People):
def __init__(self, name, age, sex, score=0):
super(Student,self).__init__(name,age,sex)
self.score = score
def choose_course(self):
print('%s choosing course' % self.name)
多态
多态指的是同一种/类事物的不同形态,在多态的背景下,可以在不用考虑对象具体类型的前提下而直接使用对象
多态性的精髓:统一
import abc
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod
def speak(self):
pass
def run(self):
print('running')
# Animal() # 父类只是用来建立规范的,不能用来实例化的,更无需实现内部的方法
class People(Animal):
def speak(self):
print('say hello')
class Dog(Animal):
def speak(self):
print('汪汪汪')
class Pig(Animal):
def speak(self):
print('哼哼哼')
obj1=People()
obj2=Dog()
obj3=Pig()
obj1.speak()
obj2.speak()
obj3.speak()
obj1.run()
'''
输出:
say hello
汪汪汪
哼哼哼
running
'''
python崇尚鸭子类型
class Disk:
def read(self):
print('Disk read')
def write(self):
print('Disk write')
class Memory:
def read(self):
print('Mem read')
def write(self):
print('Mem write')
class Cpu:
def read(self):
print('Cpu read')
def write(self):
print('Cpu write')
obj1=Disk()
obj2=Memory()
obj3=Cpu()
obj1.read()
obj2.read()
obj3.read()
封装
把一堆数据属性与方法属性 整合 到对象里面。
隐藏:
__属性 ---> 把属性变形了,即隐藏起来了!
# 封装函数属性:隔离复杂度
class ATM:
def __card(self):
print('插卡')
def __auth(self):
print('用户认证')
def __input(self):
print('输入取款金额')
def __print_bill(self):
print('打印账单')
def __take_money(self):
print('取款')
def withdraw(self):
self.__card()
self.__auth()
self.__input()
self.__print_bill()
self.__take_money()
a=ATM()
a.withdraw()
property装饰器是用来将类内的函数属性伪装成数据属性
class A:
# 类的方法:__开头的方法,在外界不能通过 get_money | __get_money 直接访问:对外隐藏了
@classmethod
def __get_money(cls):
print('输入密码,取出100w零花钱')
# 对象的方法:一般的实现需求都是,这些方法只在内部使用
def __test(self):
pass
# __money被封装,外界还是可以通过 对象.money 取值赋值
def __init__(self, money)
self.__money = money
# 取值
@property # 在外界可以 对象.money 进行取值
def money(self):
# print('走方法拿到的money')
return self.__money
# 赋值
@money.setter # 在外界可以 对象.money = 新值 进行赋值
def money(self, money):
self.__money = money
# 删除
@money.deleter
def money(self):
del self.__money
绑定方法:绑定给谁就应该由谁来调用,谁来调用就会将谁当做第一个参数传入
1.绑定给对象的方法: 类中定义的函数默认就是绑定给对象的
2.绑定给类的方法: 为类中定义的函数加上一个装饰器classmethod
非绑定方法: 既不与类绑定,又不与对象绑定,意味着对象和类都可以来调用,无论谁来调用都是一个普通的函数,没有自动传值的效果
# settings
IP='10.10.10.11'
PORT=3306
import settings
class MySql:
def __init__(self, ip, port):
self.id = self.create_id()
self.ip = ip
self.port = port
def tell_info(self):
print('<id:%s ip:%s port:%s>' % (self.id, self.ip, self.port))
@classmethod
def from_conf(cls):
return cls(settings.IP, settings.PORT)
@staticmethod
def create_id():
import uuid
return uuid.uuid4()
obj2 = MySql.from_conf()
obj2.tell_info()
'''
输出:<id:0adaa769-c7ba-4015-8928-5c8be9afd714 ip:10.10.10.11 port:3306>
'''
组合
组合指的是一个对象拥有某一个属性,该属性的值是来自于另外一个类的对象,是为了解决类与类之间代码冗余的问题。
class Teacher:
def __init__(self, name, age):
self.name = name
self.age = age
t1 = Teacher("Ben", 17)
class Student:
# 学生可以有 老师 属性
def __init__(self, name, age, teacher):
self.name = name
self.age = age
# 组合
self.teacher = teacher
stu = Student('Bob', 18, t1)
# 访问老师具体的信息
print(stu.teacher.name)
print(stu.teacher.age)
'''
输出:
Ben
17
'''
issubclass 和 sinstance
issubclass() 方法用于判断参数 class 是否是类型参数 classinfo 的子类。
class Bar:
pass
class Foo(Bar):
pass
print(issubclass(Foo,Bar))
# 返回 True
isinstance(obj,cls)检查是否obj是否是类 cls 的对象
class Foo(object):
pass
obj = Foo()
isinstance(obj, Foo)
# 返回 True
反射
通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
hasattr()
判断对象或者类的属性是否存在!
getattr()
获取对象或者类的属性
参数1: 对象
参数2: ‘属性名’
参数3: 默认值
setattr()
设置对象或者类的属性
delattr()
删除对象或者类的属性
class People:
school = 'school'
def __init__(self, name):
self.name = name
p = People('peter')
print(hasattr(p, 'school_s')) # False
print(hasattr(p, 'school')) # True
print(hasattr(People, 'school')) # True
print(getattr(People, 'name', 'Bob')) # Bob
print(getattr(People, 'school', 'Bob')) # school
print(getattr(p, 'name', 'Bob')) # peter
# print(p.age) # 报错!
setattr(p, 'age', 18)
print(p.age) # 18
print(p.name) # peter
delattr(p, 'name')
# print(p.name) # 报错!
内置方法
__init__(): 调用类的时候自动触发__init__。
__str__(): 打印对象的时候出发此方法。 (此方法内,必须return一个字符串)
__del__(): 在对象 占用空间被清掉了,会自动触发__del__方法的执行。
__setattr__(): 修改对象的属性会自动触发此方法的执行。
__deleter__: 删除属性的时候会自动触发。
__call__: 调用对象的时候会自动触发。 讲元类的时候会有用!