一、面向对象与面向过程
面向对象与面向过程是两种不同的编程范式,编程范式指的是按照什么方式去编程,去实现一个功能。不同的编程范式本质上代表对各种类型的任务采取不同的解决问题的思路。
1、面向过程编程
角色是执行者。把一个项目(事情)按照一定的顺序,从头到尾一步步做下去。这种思想好理解,但只要前面有一个步骤变了,后面的就也要变,不易维护。
2、面向对象编程
角色是指挥者。把一个项目(事情)分成一个个小部分,每一个部分负责一方面功能,最后由这些部分组合而成为一个整体。类似一个机关,分为各个职能部门,只要符合一定前提就行了。面向对象的思想适合多人的分工合作。
面向对象是包含面向过程的思路的,比如定义类中的方法,每一个小方法小功能中还是面向过程的思想。
函数编程与OOP的主要区别就是OOP可以使程序更加容易扩展和易更改。
二、面向对象中的小概念
1、类
Class,相当于一个种类,一个模型。一个类就是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象都具备的属性、共同的方法。
2、对象(实例)
Object,模型造出来的具体东西。一个对象就是一个类实例化后的实例。一个类必须经过实例化后才能在程序中调用,一个类可以实例化多个对象,每个对象也可以有不同的属性。
3、实例化
初始化一个类,造一个对象。把一个类变成一个具体的对象的过程,叫实例化。
简单定义一个类来说明以上概念,类定义使用class关键字,类名首字母大写。
class Baby:#经典类 nationnality = 'China'#类属性 def __init__(self,name):#构造函数,非必须 self.name = name,#实例属性 self.face = 1, print('实例创建时执行') def __del__(self):#析构函数,非必须 print('实例销毁前执行') def eat(self):#方法 print('want milk') class car(object):#新式类 def drive(self): pass #在python3里经典类和新式类没有任何区别,均是广度优先 #python2中经典类在多继承的时候是深度优先,新式类是广度优先 Amy = Baby('A') #实例化,Amy就是Baby这个类的实例 #构造函数里面制定了必须传name,在实例化的时候就要把name传进去 #实例化需要变量接收,不然实例化结束,实例销毁 print(Amy.face)#调用实例属性 Amy.eat()#调用实例方法
4、属性
属性就是类里面的一个变量。有类变量和实例变量,类变量是类在定义的时候就有的,实例变量是在实例化的时候才产生的变量。举个例子来说明类变量与实例变量
类变量:公共的变量,每个实例都可以用
直接通过类名.xxx来进行修改,不需要实例化
class Baby: nationnality = 'China'#类属性,公共的变量,在外面都可以用 def __init__(self,name): self.name = name,#实例属性,每个实例name不一样 # self.country = 'China'#若每个对象都会存一个一样country,比较浪费内存,变为类变量 Amy = Baby('小A') print(Amy.name) Amy.sex = 'famale'#为实例新增实例属性 print(Amy.sex) Amy.nationnality = 'USA'#不会改变类变量,只影响实例里的变量 print(Amy.nationnality)#USA Ben =Baby('小B') print(Ben.nationnality)#China Baby.nationnality = 'UK'#类变量直接通过类名.xxx来进行修改,不需要实例化 print(Amy.nationnality)#USA,Amy的实例化发生在修改类变量之前,所以不再改变 print(Ben.nationnality)#UK Cindy = Baby('小C') print(Cindy.nationnality)#UK
5、方法
方法就是类的功能,也就是定义在类里面的函数。
(1) 类方法(cls):@classmethod
a、不用实例化就可以直接调用
b、它可以通过cls使用类变量
c、不能调用类中其他实例方法和实例变量
d、不想实例化的时候,就可以定义成类方法
(2) 实例方法(self)
实例化后才能用的方法
class Baby: nationnality = 'China'#类变量 def __init__(self,name): self.name = name,#实例变量 def cry(self):#实例方法 print('555...') @classmethod #类方法 def lei(cls):#cls代表的就是Baby print(cls.nationnality) # print(cls.name)#报错,不能调用类中其他实例变量 # cls.cry()#报错,不能调用类中其他实例方法 Amy = Baby('小A') # Baby.cry()#报错,带self的方法均是实例方法,需要实例化后才能调用 Baby.lei()#不实例化,直接用类名调用这个lei的方法 Amy.lei()#实例化后,再通过对象调用类方法
(3) 属性方法(self):@property
看起来像属性的一个方法,将没有入参的函数变为一个属性方法(类似于变量),结果是函数的返回值
class Baby: @property #属性方法 def smile(self): return 'haha' Amy = Baby('小A') print(Amy.smile)#属性方法像属性一样调用 # print(Amy.smile())#报错,调用属性方法不用加括号
(4) 静态方法():@staticmethod
静态方法就是一个普通的函数,只不过是写在类里面而已,它用不了类变量、类方法、实例变量、实例方法。
class Baby: @staticmethod def jing():#括号中未自动填充cls、self print('这是静态方法,它和一个没写在类里面的函数一样') Amy = Baby('小A') Amy.jing()#调用静态方法
6、继承
一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承。子类可以重写父类方法。继承是为了代码重用。
python3中多继承都是广度优先,python2中经典类的多继承是深度优先,新式类的多继承是按照广度优先的。
class Daddy(object): def __init__(self): self.house = 1 def drive(self): print('会开车') def dance(self): print('跳广场舞') class Son(Daddy):#继承 def dance(self):#重写父类方法 print('跳鬼步舞') s = Son() print(s.house)#1 s.drive()#会开车 s.dance()#跳鬼步舞
通过定义父类属性或者方法,子类继承,可以代码重用,例如将数据库连接的属性定义在一个Base的父类中
class Base(object): def __init__(self,host,port,password): self.host = host, self.port = port, self.password =password class Mysql(Base): pass class Redis(Base): pass
7、封装
把一些功能的实现细节不对外暴露,类中对数据的赋值、内部调用对外部用户是透明的,使类变成一个胶囊或容器。里面包含着类的数据和方法。
比如说造的一个人,你把他身体内部的什么心肝脾肺肾都封装起来了,其他人都看不到,你直接找这个人。
8、多态
对不同类的对象发出相同的消息将会有不同的行为。例如,组长说大家喝点东西吧,小A喝茶,小B喝果汁,小C喝水。
多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。一种接口,多种实现。
三、本类对象 :self
类中的self代表的是本类对象。即self代表的是实例化之后的对象。
因为函数里面的变量都是局部变量,出了函数就不能用了。用self给对象绑定了之后,就可以在类中self.xx随便用了
class Baby: def __init__(self,name): #name = name#局部变量,出了函数就失效 self.name = name#在类中self.name可以随便用了 print('self的内存地址', id(self)) def cry(self): print('%s在哭'%self.name)#self.name在类中其他方法可以使用 Amy = Baby('小A')#实例化时将Amy的地址给self print('实例的内存地址',id(Amy))#与实例化时self的内存地址一致 Amy.cry()
四、构造函数与析构函数
1、构造函数:def __int__()
构造函数是类在实例化(初始化)时,自动做的操作,不是必须的
实例化时只有构造函数会被自动执行,其他函数不会被自动执行
class Baby: def __init__(self,name):#构造函数 self.name = name def cry(self): self.action = '哭了' print(self.action) Amy = Baby('小A') print(Amy.name)#实例化时构造函数自动执行, print(Amy.action)#cry方法未被执行,直接调用Amy.action会报错,object has no attribute对象没有该属性
解决Amy.action报错的方法有两种(解决函数未被调用,属性未生成问题):
(1) 将cry这个方法放在构造函数里,这样实例化的时候函数会被执行,Amy.action属性就生成了
class Baby: def __init__(self,name):#构造函数 self.name = name self.cry()#将cry这个方法放在构造函数里 def cry(self): self.action = '哭了' print(self.action) Amy = Baby('小A') print(Amy.action)#不再报错
(2) 在实例化对象后,先调用cry这个方法,在去打印Amy.action属性
class Baby: def __init__(self,name):#构造函数 self.name = name def cry(self): self.action = '哭了' print(self.action) Amy = Baby('小A') Amy.cry()#先调用cry这个方法 print(Amy.action)
2、析构函数:def __del__()
实例被销毁的时候执行,不是必须的
3、例子:操作Mysql
import pymysql class MySQL(object): def __init__(self,host,user,passwd,db,port=3306,charset='utf8'):#构造函数,类实例化的时候执行 try: self.conn = pymysql.connect( host = host,user=user,passwd=passwd,db=db,port=port,charset=charset, autocommit=True # 自动提交,执行insert,update语句时,可以自动提交 ) except Exception as e: print('数据库连接失败,%s'%e) else: self.cur = self.conn.cursor(cursor=pymysql.cursors.DictCursor) def __del__(self):#析构函数,实例被销毁的时候执行 self.cur.close() self.conn.close() print('数据库连接关闭') def ex_sql(self,sql): try: self.cur.execute(sql) except Exception as e: print('sql语句错误,%s'%sql) else: self.res = self.cur.fetchall() return self.res #有没有返回值都可以 my = MySQL('127.0.0.1', 'root', '123456', 'data') my.ex_sql('select * from stu') print(my.res)#可以用实例属性取值 # print(my.ex_sql('select * from stu'))#也可以用实例方法的返回值 print('我是最后一行代码')#执行完最后一行代码,数据库连接关闭
五、私有
出了类之后就不能再使用的变量和方法,被称为私有变量、私有方法。
有些重要的信息不想被调用获取,可以加两个下划线"__",将变量或者方法变为私有。
import redis class Si(object): def __init__(self): self.__host = '127.0.0.1'#私有变量 self.port = 6379 def open(self): print('实例方法') self.__close()#类内部可以调用私有方法 def __close(self):#私有方法 print('私有方法') m = Si() # print(m.__host)#报错,没有__host这个属性,私有变量出类后不能再使用 print(m.port) m.open()#打印出:实例方法,私有方法 # m.__close()#报错,没有这个方法,,私有方法出类后不能再使用
例子:操作Redis(将redis的访问变量私有)
import redis class MyRedis(object): def __init__(self): self.__host = '127.0.0.1' self.__passwd ='' self.__db = 1 self.__port = 6379 self.r = redis.Redis(host=self.__host,password=self.__passwd,db=self.__db,port=self.__port)
#这时并不会去连redis #所以捕捉不到数据库连接失败的异常 def str_get(self,k):#string类型获取value res = self.r.get(k)#get不存在的值数据库不会报错,返回NULL if res: return res.decode() #不用return None,函数默认返回None def str_set(self,k,v,time=None):#string类型添加修改key self.r.set(k,v)#set数据库不会报错,不需要捕捉异常 def str_del(self,k):#string类型删除key tag = self.r.exists(k) if tag: self.r.delete(k)#删除不存在的key,redis返回0,不报错 print('删除成功') else: print('这个key不存在') def hash_get(self,bk,sk=None):#hash类型获取value if sk: res = self.r.hget(bk,sk)#指定大key和小key获取对应的数据 if res: return res.decode() else: res = self.r.hgetall(bk)#获取里面所有的key和value,获取结果是字典 if res: res_d = {} for k, v in res.items(): res_d[k.decode()]=v.decode() return res_d def hash_set(self,bk,sk,v):#hash类型set self.r.hset(bk,sk,v)#get不到值时不会报错,所以不用捕捉异常 def hash_del(self,bk,sk=None):#hash类型del if sk:#删除指定的key, res = self.r.hdel(bk,sk)#删除不存在的,不会报错返回0,不用捕捉异常,删除成功返回1 if res: print('删除成功') return 1 else: print('删除失败,该key不存在') return 0 else:#删除整个key res= self.r.delete(bk) if res: print('删除成功') return 1 else: print('删除失败,该key不存在') return 0 @property def clean_redis(self): self.r.flushdb() # 清空redis,不用获取所有值再挨个删除 print('清空redis成功!') m = MyRedis() m.str_set('Kity','famale') print(m.str_get('Kity')) m.str_del('Kity') m.hash_set('toy','car','red') m.hash_set('toy','ball','blue') print(m.hash_get('toy','car')) print(m.hash_get('toy')) m.hash_del('toy','car') m.hash_del('toy') m.clean_redis