面向对象
一、面向对象编程思想
1、面对对象简介:
面向对象(Object Oriented,OO)是软件开发方法。面向对象的概念和应用已超越了程序设计和软件开发,
扩展到如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD技术、人工智能等
领域。面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。
2、面对对象编程定义:对象:特征和技能的集合体,即基于这种集合体进行编程
3、编程思想:在编程过程中我们要定义一个个类,类中有我们定义的一个个特征以及技能,用我们写得类
来创造出一个个对象
二、类
1、类的定义:类是用来定义一个个特征和技能的抽象概念
2、类的作用:通过调用类可以产生拥有这些特征与技能的对象
3、使用须知:在程序中要先有类再有对象
4、优缺点:优点:拓展性 缺点;复杂度高
语法
class Student:#类名首字母必须大写
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
def learn(self):
print('学生开始看书了')
三、对象
1、定义:拥有特征和技能的结合体
2、如何产生对象:调用类生成,也称为类的实例化
3、对象生成过程:
1、首先会产生一个空对象stu1
2、会自动触发类内部的__init__函数
3、然后将空对象stu1连同调用类时括号内的参数组成(stu1,"马冬梅",18,'female'),将这四个参数
一起传给__init__函数
4、类内部定义的变量是给所有对象共享,所有对象指向的都是同一个内存地址
5、类内部的方法是绑定给对象的,不同的对象绑定值不一样,即绑定方法不一样
对象1:
特征:
school="Oldboy"
name="马冬梅"
age=18
sex="female"
技能:
学习
选课
对象2:
特征:
school="Oldboy"
name="甜蜜蜜"
age=21
sex="male"
技能:
学习
选课
对象3:
特征:
school="Oldboy"
name="原石开"
age=22
sex="male"
技能:
学习
选课
现实生活中的老男孩学生类:
相同的特征
school="Oldboy"
相同的技能
学习
选课
'''
#1、程序中的类
class OldboyStudent:
# 用变量表示特征
school="Oldboy"
# stu1, "马冬梅", 18, 'female'
def __init__(self,name,age,sex): #self=stu1 name= "马冬梅" age=18 sex="female"
# print('==========init run=============>')
self.name=name # stu1.name = "马冬梅"
self.age=age # stu1.age = 18
self.sex=sex # stu1.sex = "female"
# 用函数表示技能
def learn(self):
print('is learning...',self)
def choose(self):
print('choose course...')
人狗大战
class People:
def __init__(self,name,atk,hp):
self.name=name
self.atk=atk
self.hp=hp
def bite(self,enemy):
enemy.hp-= self.atk
print('''
人%s咬了狗%s
狗掉血%s
狗剩余血量%s
'''%(self.name,enemy.name,self.atk,enemy.hp))
class Dog:
def __init__(self,name,agg,atk,hp):
self.name=name
self.agg=agg
self.atk=atk
self.hp=hp
def bite(self,enemy):
enemy.hp-=self.atk
print('''
狗%s咬了人%s
人掉血%s
人剩余血量%s
'''%(self.name,enemy.name,self.atk,enemy.hp))
peo=People('钱泽亮',200,200)
dog1=Dog('旺财','京巴',20,30)
peo.bite(dog1)
dog1.bite(peo)
四、面对对象三大特性
1、继承
1、面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编
写原来的类的情况下对这些功能进行扩展。
2、通过继承创建的新类称为“子类”或“派生类”。
3、被继承的类称为“基类”、“父类”或“超类”。
4、继承的过程,就是从一般到特殊的过程。
5、要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
6、在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
7、继承概念的实现方式主要有2类:实现继承、接口继承。
1、实现继承是指使用基类的属性和方法而无需额外编码的能力;
2、接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力(子类重构爹类方法);
8、在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee 是一个人,Manager 也是一个人,因此这两个类都可以继承
Person 类。但是 Leg 类却不能继承 Person 类,因为腿并不是一个人。
继承示例(1)
#BlackPerson类 继承 Person类 #动作: #BlackPerson可调用Person的talk方法 class Person(object): def talk(self): print("person is talking...l") class BlackPerson(Person): pass b = BlackPerson() b.talk() #输出结果 #person is talking...l
继承示例(2)
#BlackPerson类 继承 Person类 #动作: #BlackPerson继承 新增方法walk #BlackPerson继承 重写父类talk class Person(object): def talk(self): print("person is talking...1") class BlackPerson(Person): def talk(self): print("person is talking...2") def walk(self): print("is walking....") b = BlackPerson() b.talk() b.walk() #输出结果 #person is talking...2 #is walking....
继承式列(3)
#BlackPerson类 WhitePerson 类 继承 Person类 #动作1: #Person父类 添加name,age元素 #注:父类添加元素后,对象也许添加元素否则报错 # #动作2: #BlackPerson类新建构造函数,并继承父类构建函数,先继承后重组。 #注:子类需要有父类构造函数内的元素,后可新建元素,调用父类 # 成员属性下,添加构造函数。
#
#原理: #注:如图1,当子类函数,获取到name,age元素时,会传给下面父类的 # 子类元素,父类获取到子类元素会把传到的值,划分给构建函数内的 # 成员属性,完成先继承后重组的步骤。
#
#动作3:
#注:在子类继承父类后,可新建自己类的成员属性
#
#动作4:
#子类方法下也可使用父类的继承方法
#注:父类.方法(self) 但这样做没有意义。
class Person(object): def __init__(self,name,age): self.name = name #self = b self.name = b.name self.age = age self.sex = "noraml" def talk(self): print("person is talking...1") class BlackPerson(Person): def __init__(self,name,age ,strength): #先继承,在重构(动作1) Person.__init__(self,name,age) #父类调用子类元素(动作2)
self.strength = tering #继承父类后可调用自己的成员元素(动作3)
def talk(self):
Person.tslk(self) #调用父类方法(动作4) print("person is talking...2") def walk(self): print("is walking....") b = BlackPerson("Kevin","22") class WhitePerson(Person): pass
类的继承(二)
实战案例
class SchoolMember(object): #基类 '''学校成员基类''' member = 0 #注册成员数 def __init__(self,name,age,sex): #基类构造函数 self.name = name self.age = age self.sex = sex self.enroll() #注册方法,直接调用给子类 def enroll(self): #注册成员,每多出一个对象就+1 '''注册''' print("just enrolled a new school member [%s]"%self.name) SchoolMember.member += 1 def tell(self): #问题2.在不改写子类的情况下,通过基类打印,不同子类的信息。 """循环打印人员信息""" print("-------info:%s---------"%self.name) for k,v in self.__dict__.items(): print("\t",k,v) def __del__(self): #问题1,删除成员后自动减注册表。 '''析构函数,开除一个人后注册表-1''' print("开除了[%s]..."%self.name) SchoolMember.member -=1 #每有一个对象删除就减1 class Teacher(SchoolMember): #子类 '''讲师类''' def __init__(self,name,age,sex,salary,course): #包含基类并新建元素 SchoolMember.__init__(self,name,age,sex) #继承基类构造 self.salary = salary self.course = course def teching(self): #方法 print("Teacher[%s] is teaching [%s]"%(self.name,self.course)) class Student(SchoolMember): #子类 """学生""" def __init__(self,name,age,sex,course,tuition): #包含基类并新建元素 SchoolMember.__init__(self,name,age,sex) #继承基类构造 self.course = course self.tuition = tuition #fee学费 def pay_tuition(self,amount): print("student [%s] has just paied [%s]"%(self.name,amount)) self.amount += amount t1 = Teacher("Wusir","28","F*M","8000","Python") s1 = Student("HaiTao","38","N/A","PYS15",300000) s2 = Student("LiChuang",12,"M","PYS15",11000) print(t1.__dict__) #打印变量t1的元素信息。 #问题2.在不改写子类的情况下,通过基类打印,不同子类的信息。 # t1.tell() # s1.tell() #执行结果 # -------info:Wusir--------- # name Wusir # age 28 # sex F*M # salary 8000 # course Python # # -------info:HaiTao--------- # name HaiTao # age 38 # sex N/A # course PYS15 # tuition 300000 #问题1,删除成员后自动减注册表。 # import time # del s2 # print(SchoolMember.member) # time.sleep(5) #执行结果: # 开除了[LiChuang]... #2 #开除了[Wusir]... #开除了[HaiTao]...
注:析构函数,程序执行完毕后会自动输出。
继承写法
SchoolMember.__init__(self,name,age,sex) #经典类
super(Teacher, self).__init__(name,age,sex) #新式类
#菱形情况新式类广度优先
class A:
def f1(self):
print('这是A.f1')
class B(A):
def f1(self):
print('这是B.f1')
class C(B):
def f1(self):
print('这是C.f1')
pass
class D(A):
def f1(self):
print('这是D.f1')
class E(D):
def f1(self):
print('这是E.f1')
class F(C,E):
def f1(self):
print('这是F.f1')
b=F()
b.f1()
多继承
class School(object): #多继承类 '''学校类''' def info(self,addr): print("%s 6666666666666666"%addr) class Teacher(SchoolMember,School): #多继承 '''讲师类''' def __init__(self,name,age,sex,salary,course): #包含基类并新建元素 SchoolMember.__init__(self,name,age,sex) #继承基类构造
2、封装
1、从封装本身的意思去理解,封装就好像是拿来一个麻袋,把小猫,小狗,小王八,还有alex一起装进麻袋,然后把麻袋封上口子。
照这种逻辑看,封装=‘隐藏’,这种理解是相当片面的
2、封装就是隐藏属性,可是将属性全部隐藏根本就没什么用,所以又不是字面意义上的隐藏,python封装语法就是属性名开头加双下划綫,
封装的过程就是将属性名变形,而在类定义阶段属性名就已经丢入名称空间中所以封装只针对类的外部,外部想靠类的字面属性名调用,
· 是调用不到的,如果要强制调用,只能按照变形规则进行访问(_类名__属性名)
3、封装的作用:
1:封装数据:将数据隐藏起来这不是目的。隐藏起来然后对外提供操作该数据的接口,然后我们可以在接口附加上对该数据操作的
限制,以此完成对数据属性操作的严格控制。
2:封装方法:目的是隔离复杂度
#让子类的属性覆盖不了父类属性
#正常情况
>>> class A:
... def fa(self):
... print('from A')
... def test(self):
... self.fa()
...
>>> class B(A):
... def fa(self):
... print('from B')
...
>>> b=B()
>>> b.test()
from B
#把fa定义成私有的,即__fa
>>> class A:
... def __fa(self): #在定义时就变形为_A__fa
... print('from A')
... def test(self):
... self.__fa() #只会与自己所在的类为准,即调用_A__fa
...
>>> class B(A):
... def __fa(self):
... print('from B')
...
>>> b=B()
>>> b.test()
from A
#数据接口
class Teacher:
def __init__(self,name,age):
# self.__name=name
# self.__age=age
self.set_info(name,age)
def tell_info(self):
print('姓名:%s,年龄:%s' %(self.__name,self.__age))
def set_info(self,name,age):
if not isinstance(name,str):
raise TypeError('姓名必须是字符串类型')
if not isinstance(age,int):
raise TypeError('年龄必须是整型')
self.__name=name
self.__age=age
t=Teacher('egon',18)
t.tell_info()
t.set_info('egon',19)
t.tell_info()
#隔离复杂度
#取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
#对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
#隔离了复杂度,同时也提升了安全性
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()
封装与拓展性:封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。
#类的设计者
class Room:
def __init__(self,name,owner,width,length,high):
self.name=name
self.owner=owner
self.__width=width
self.__length=length
self.__high=high
def tell_area(self): #对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积
return self.__width * self.__length
#使用者
>>> r1=Room('卧室','egon',20,20,20)
>>> r1.tell_area() #使用者调用接口tell_area
#类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码
class Room:
def __init__(self,name,owner,width,length,high):
self.name=name
self.owner=owner
self.__width=width
self.__length=length
self.__high=high
def tell_area(self): #对外提供的接口,隐藏内部实现,此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,而且外部调用感知不到,仍然使用该方法,但是功能已经变了
return self.__width * self.__length * self.__high
#对于仍然在使用tell_area接口的人来说,根本无需改动自己的代码,就可以用上新功能
>>> r1.tell_area()
3、多态
1、多态是面向对象语言的一个基本特性,多态意味着变量并不知道引用的对象是什么,根据引用对象的
不同表现不同的行为方式。在处理多态对象时,只需要关注它的接口即可,python中并不需要显示的
编写(像Java一样)接口,在使用对象的使用先假定有该接口,如果实际并不包含,在运行中报错。
class handGun():
def __init__(self):
pass
def fire(self):
print 'handGun fire'
class carbine():
def __init__(self):
pass
def fire(self):
print 'carbine fire'
import handGun
import carbine
class gunFactory():
def __init__(self,gun_type):
self.gun_type = gun_type
def produce(self):
if handGun == self.gun_type:
return handGun.handGun()
else:
return carbine.carbine()
客户端
fa = gunFactory(handGun)
gun = fa.produce()
/*只要是枪,就认为它具有开火的功能,如果没有开火的功能,程序运行中就报错*/
gun.fire()
规范属性
import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
@abc.abstractmethod
def talk(self):
pass
class People(Animal): #动物的形态之一:人
def talk(self):
print('say hello')
class Dog(Animal): #动物的形态之二:狗
def talk(self):
print('say wangwang')
class Pig(Animal): #动物的形态之三:猪
def talk(self):
print('say aoao')
五、绑定与非绑定
1、 类中定义的函数分成两大类
1、绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入):
1. 绑定到类的方法:用classmethod装饰器装饰的方法。
为类量身定制
类.boud_method(),自动将类当作第一个参数传入
(其实对象也可调用,但仍将类当作第一个参数传入)
2. 绑定到对象的方法:没有被任何装饰器装饰的方法。
为对象量身定制
对象.boud_method(),自动将对象当作第一个参数传入
(属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)
2、非绑定方法:用staticmethod装饰器装饰的方法
1. 不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。就是一个普通工具而已
注意:与绑定到对象方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数
,对象调用该方法会自动传值,而staticmethod装饰的方法,不管谁来调用,都没有自动传值一说
2 、绑定方法
绑定给对象的方法:类的下面的函数,类产生的对象就会和他绑定
绑定给类的方法(classmethod)
classmehtod是给类用的,即绑定到类,类在使用时会将类本身当做参数传给类方法的第一个参数(即便是对象来调用也会将类当作第
一个参数传入),python为我们内置了函数classmethod来把类中的函数定义成类方法
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() #对象也可以调用,但是默认传的第一个参数仍然是类
3、 非绑定方法
在类内部用staticmethod装饰的函数即非绑定方法,就是普通函数
statimethod不与类或对象绑定,谁都可以调用,没有自动传值效果
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> #查看结果为普通函数
4、 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__的执行:
定义MySQL类
1.对象有id、host、port三个属性
2.定义工具create_id,在实例化时为每个对象随机生成id,保证id唯一
3.提供两种实例化方式,方式一:用户传入host和port 方式二:从配置文件中读取host和port进行实例化
4.为对象定制方法,save和get_obj_by_id,save能自动将对象序列化到文件中,文件路径为配置文件中DB_PATH,文件名为id
号,保存之前验证对象是否已经存在,若存在则抛出异常,;get_obj_by_id方法用来从文件中反序列化出对象
#settings.py内容
'''
HOST='127.0.0.1'
PORT=3306
DB_PATH=r'E:\CMS\aaa\db'
'''
import settings
import uuid
import pickle
import os
class MySQL:
def __init__(self,host,port):
self.id=self.create_id()
self.host=host
self.port=port
def save(self):
if not self.is_exists:
raise PermissionError('对象已存在')
file_path=r'%s%s%s' %(settings.DB_PATH,os.sep,self.id)
pickle.dump(self,open(file_path,'wb'))
@property
def is_exists(self):
tag=True
files=os.listdir(settings.DB_PATH)
for file in files:
file_abspath=r'%s%s%s' %(settings.DB_PATH,os.sep,file)
obj=pickle.load(open(file_abspath,'rb'))
if self.host == obj.host and self.port == obj.port:
tag=False
break
return tag
@staticmethod
def get_obj_by_id(id):
file_abspath = r'%s%s%s' % (settings.DB_PATH, os.sep, id)
return pickle.load(open(file_abspath,'rb'))
@staticmethod
def create_id():
return str(uuid.uuid1())
@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.save()
conn1=MySQL('127.0.0.1',3306)
conn1.save() #抛出异常PermissionError: 对象已存在
obj=MySQL.get_obj_by_id('7e6c5ec0-7e9f-11e7-9acc-408d5c2f84ca')
print(obj.host)
您的资助是我最大的动力!
金额随意,欢迎来赏!