python 对象与类

https://www.cnblogs.com/linhaifeng/articles/6182264.html

一.编程进化论
https://www.cnblogs.com/linhaifeng/articles/6428835.html

1.最开始无组织无架构,从简单控制流中按部写指令

2.从上述指令提取重复的代码块/逻辑,组织到一起(如定义函数),实现代码重用,走向结构化,更具逻辑性

3.定义函数都是独立于函数外定义变量后作为参数传给函数:数据和动作是分离的

4.如果把数据和动作内嵌到一个结构(如函数/类)里,就有了一个对象系统

二.面向对象
1.三大编程范式(方法论,编程风格):面向过程编程/函数式编程/面向对象编程

2.面向对象的程序设计:核心是对象二字,基于面向对象设计程序就好比在创造一个世界,将一类具体事物的数据和动作整合到一起。与面向过程机械式的思维方式相比,面向对象更加注重对现实世界的模拟

面向对象设计:
def dog(num,gender,type):   #类
    def bark(dog):
        print('一条狗%s在叫'%dog['num'])
    def run(dog):
        print('一条%s狗在跑'%sdog['type'])
    def init(num,gender,type)   #用于初始化:把变量赋为默认值,把控件设为默认状态,把没准备的准备好
        dog1={'num':num,'gender':gender,'type':type,'bark':bark,'run':run}   
        return dog
    return init(num,gender,type)
    
d1=dog('1','male','Tibetan Mastiff')   #对象
d2=dog('2','female','Husky')
d1['bark'](d1)
d2['run'](d2)

3.面向对象编程(object-oriented programming):用定义类+实例/对象的方式去实现面向对象的设计

#用面向对象编程独有的语法class去实现面向对象设计
class dog:
    def _init_(self,name,gender,type):
        self.name=name
        self.gender=gender
        self.type=type    
    def bark(self):   #self参数提示运行时传入自身
        print('一条编号为%s的%s在叫'%(self.name,self.type))
    def bite(self):
        print('%s%s在咬人'%(self.gender,self.type))
    def eat(self):
        print('%s%s在吃'%(self.gender,self.type))
dog1=dog('1','male','Pekingese')
dog2=dog('2','female','Dachshund')
dog3=dog('3','male','Tibetan Mastiff')

dog1.bark()
dog2.bite()
dog3.eat()
#class无需return

优点:解决了程序的扩展性,对某个对象的修改会立刻反映到整个体系,如对游戏中一个人物参数/技能修改都很容易;通过封装明确区分了内外;通过继承+多态在语言层面支持了归一化设计

缺点:复杂度远高于面向过程,不了解面向对象而立即上手,易出现过度设计。一些扩展性要求低的场景使用面向对象会徒增编程难度,如管理linux系统的shell脚本,面向过程就更加适合;无法向面向过程可以很精准的预测问题的处理流程与结果。于是经常看到对战类游戏,新增一个游戏人物,在对战的过程中极易出现阴霸的技能,一刀砍死3个人

应用:需求经常变化的软件,一般需求的变化都集中在用户层(互联网应用/企业内部软件/游戏)

面向对象的程序设计并不是全部:对于一个软件质量来说,面向对象的程序设计只是用来解决扩展性

用面向对象语言写程序(如class)!=面向对象编程

#数据与专门操作该数据的功能组合到一起:

#1、在没有学习类这个概念时,数据与功能是分离的
def exc1(host,port,db,charset):
    conn=connect(host,port,db,charset)
    conn.execute(sql)
    return xxx
def exc2(host,port,db,charset,proc_name)
    conn=connect(host,port,db,charset)
    conn.call_proc(sql)
    return xxx

#每次调用都需要重复传入一堆参数
exc1('127.0.0.1',3306,'db1','utf8','select * from tb1;')
exc2('127.0.0.1',3306,'db1','utf8','存储过程的名字')

#2、我们能想到的解决方法是,把这些变量都定义成全局变量
HOST=127.0.0.1’
PORT=3306
DB=‘db1’
CHARSET=‘utf8’

def exc1(host,port,db,charset):
    conn=connect(host,port,db,charset)
    conn.execute(sql)
    return xxx
def exc2(host,port,db,charset,proc_name)
    conn=connect(host,port,db,charset)
    conn.call_proc(sql)
    return xxx

exc1(HOST,PORT,DB,CHARSET,'select * from tb1;')
exc2(HOST,PORT,DB,CHARSET,'存储过程的名字')

#3、但是2的解决方法也是有问题的,按照2的思路,我们将会定义一大堆全局变量,这些全局变量并没有做任何区分,即能够被所有功能使用,然而事实上只有HOST,PORT,DB,CHARSET是给exc1和exc2这两个功能用的。言外之意:我们必须找出一种能够将数据与操作数据的方法组合到一起的解决方法,这就是我们说的类了

class MySQLHandler:
    def __init__(self,host,port,db,charset='utf8'):
        self.host=host
        self.port=port
        self.db=db
        self.charset=charset
    def exc1(self,sql):
        conn=connect(self.host,self.port,self.db,self.charset)
        res=conn.execute(sql)
        return res
    def exc2(self,sql):
        conn=connect(self.host,self.port,self.db,self.charset)
        res=conn.call_proc(sql)
        return res

obj=MySQLHandler('127.0.0.1',3306,'db1')
obj.exc1('select * from tb1;')
obj.exc2('存储过程的名字')

#改进
class MySQLHandler:
    def __init__(self,host,port,db,charset='utf8'):
        self.host=host
        self.port=port
        self.db=db
        self.charset=charset
        self.conn=connect(self.host,self.port,self.db,self.charset)
    def exc1(self,sql):
        return self.conn.execute(sql)
    def exc2(self,sql):
        return self.conn.call_proc(sql)

obj=MySQLHandler('127.0.0.1',3306,'db1')
obj.exc1('select * from tb1;')
obj.exc2('存储过程的名字')

4.面向对象三大特性:继承,多态,封装

三.类:python中一切皆为对象,且python3中类与类型是一个概念,类型就是类
1.概念:一系列对象相似的特征与技能的结合体

#类型dict就是类dict
>>> list
<class 'list'>

#实例化的到3个对象l1,l2,l3
>>> l1=list()
>>> l2=list()
>>> l3=list()

#三个对象都有绑定方法append,是相同的功能,但内存地址不同
>>> l1.append
<built-in method append of list object at 0x10b482b48>
>>> l2.append
<built-in method append of list object at 0x10b482b88>
>>> l3.append
<built-in method append of list object at 0x10b482bc8>

#操作绑定方法l1.append(3),就是在往l1添加3,绝对不会将3添加到l2或l3
>>> l1.append(3)
>>> l1
[3]
>>> l2
[]
>>> l3
[]
#调用类list.append(l3,111)等同于l3.append(111)
>>> list.append(l3,111) #l3.append(111)
>>> l3
[111]

2.在程序中需先定义类,后产生对象

  • 这与函数的使用是类似的,先定义函数,后调用函数,类也是一样的,在程序中需要先定义类,后调用类
  • 不一样的是,调用函数会执行函数体代码返回的是函数体执行的结果,而调用类会产生对象,返回的是对象
class 类名:
    '类的文档字符串'   #注释,说明用处
    '类体'   #初始化函数/共有功能
    
d1=类名()   #运行(实例化)
print(d1.__dict__)   #打印对象d1的字典形式#自定义类的对象
print(类名.__dict__)
#1.在程序中特征用变量标识,技能用函数标识
#2.因而类中最常见的无非是:变量和函数的定义

#程序中的类
class OldboyStudent:
    school='oldboy'
    def learn(self):
        print('is learning')
        
    def eat(self):   #类当中定义的方法默认有一个参数为self放在第一个位置
        print('is eating')
    
    def sleep(self):
        print('is sleeping')
  
#注意:
  1.类中可以有任意python代码,这些代码在类定义阶段便会执行
  2.因而会产生新的名称空间,用来存放类的变量名与函数名,可以通过OldboyStudent.__dict__查看
  3.对于经典类来说我们可以通过该字典操作类名称空间的名字(新式类有限制),但python为我们提供专门的.语法
  4.点是访问属性的语法,类中定义的名字,都是类的属性

#程序中类的用法
.:专门用来访问属性,本质操作的就是__dict__
OldboyStudent.school #等于经典类的操作OldboyStudent.__dict__['school']
OldboyStudent.school='Oldboy' #等于经典类的操作OldboyStudent.__dict__['school']='Oldboy'
OldboyStudent.x=1 #等于经典类的操作OldboyStudent.__dict__['x']=1
del OldboyStudent.x #等于经典类的操作OldboyStudent.__dict__.pop('x')

3.实例化:由类产生对象的过程,结果是一个对象(实例)

4.经典类与新式类:不同在于新式类必须继承object父类,经典类不继承object父类(python2中区分,python3中均为新式类)

class 类名(...):   #经典类
    pass

class 类名(object...):   #新式类
    pass

在这里插入图片描述
写法推荐使用新式类(包含更多功能)

5.属性:分为数据属性和函数属性,类属性又被称为静态数据/静态变量,与其所属的类绑定,不依赖类的实例

数据属性:变量,所有对象共享的
函数属性:函数(在面向对象里称为方法),绑定给对象用的

访问:类和对象均用点来访问属性
使用.来访问属性本质访问类/对象的__dict__属性字典(类的字典是由类及其实例共享的,每个实例的字典是独立的)
在这里插入图片描述

class Human:   #定义类
    genic_material='DNA'   #定义数据属性
    def living():
        print('L')
    
Human.genic_material   #调用属性
Human.living   #调用属性
print(Human.genic_material)   #访问数据属性   #本质上是到属性字典查找
Human.living()   #调用方法

dir(Human)   #查看属性
def MyData:
    pass
x=10
y=20
MyData.x=1
MyData.y=2
#实现类似函数作用域的划分

6.类的特殊属性
关于__ dict__与dir()的区别:cnblogs.com/zjchao/p/7894477.html

#python为类内置的特殊属性
类名.__name__# 类的名字(字符串)
类名.__doc__# 类的文档字符串
类名.__base__# 类的第一个父类(见继承部分),一般为object
类名.__bases__# 类所有父类构成的元组(见继承部分)
类名.__dict__# 类的字典属性   #不要直接修改字典,会导致oop不稳定
类名.__module__# 类定义所在的模块
类名.__class__# 实例对应的类(仅新式类中)

7.属性的增删查改

#类的数据属性是所有对象共享的,id都一样
print(id(OldboyStudent.school))
print(id(s1.school))
print(id(s2.school))
print(id(s3.school))

'''
4377347328
4377347328
'''

#类的函数属性是绑定给对象使用的,obj.method称为绑定方法,内存地址都不一样
#ps:id是python的实现机制,并不能真实反映内存地址,如果有内存地址,还是以内存地址为准
print(OldboyStudent.learn)
print(s1.learn)
print(s2.learn)
print(s3.learn)
'''
<function OldboyStudent.learn at 0x1021329d8>
<bound method OldboyStudent.learn of <__main__.OldboyStudent object at 0x1021466d8>>
<bound method OldboyStudent.learn of <__main__.OldboyStudent object at 0x102146710>>
<bound method OldboyStudent.learn of <__main__.OldboyStudent object at 0x102146748>>
'''

在这里插入图片描述
在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类…最后都找不到就抛出异常

class chinese:
    country='China'
    def __init__(self,name):
        self.name=name
    def play_ball(self,ball):    
        print('%s正在打%s'%(self.name,ball))
查:print(chinese.country)
改:chinese.country='CHINA'
删:del chinese.country
增:chinese.location='Asia'

8.类的内置属性(attr):如设置,使用设置的;否则使用内置的
__setattr __, __ delattr __ , __ getattr __(见python函数部分)

四.对象相关
1.定义:特征与技能(数据与函数)的结合体
本质就是一个内存块,有特定的值,支持特定类型的相关操作

2.对象构成:

#标识identity:用于唯一标识对象
通常对应于对象在内存中的地址
使用id(obj)返回obj的标识
#类型type:用于表示对象存储的数据的类型
可以限制对象的取值范围及可执行的操作
使用type(obj)返回obj的类型
#值value:表示对象存储的数据的信息
使用print(obj)直接打印出值

3._init _方法:实例化时自动运行,有一个self参数,自动return self

  • 未通过该方法进行初始化的属性当是类属性,类和对象均可访问且值均相同;通过该方法进行初始化的属性是对象属性,类无法访问,对象可以访问且不同对象的属性值不同
# 强调:
#   1.该方法内可以有任意的python代码
#   2.一定不能有返回值(return none)
class People:   #变量只找到最高的类一层,如没有则报错
    country='China'
    x=1
    def __init__(obj, name, age, sex): #obj=obj1,x='egon',y=18,z='male'
        # if type(name) is not str:
        #     raise TypeError('名字必须是字符串类型')
        obj.name = name
        obj.age = age
        obj.sex = sex#绑定得到对象属性,类无法访问
    def run(self):
        print('----->', self)

obj1=People(3537,18,'male')   
#相当于obj1=People.__init__(obj1,3537,18,'male')

# print(obj1.run)
# obj1.run() #People.run(obj1)
# print(People.run)

!!!__init__方法之为对象定制自己独有的特征

4.使用

#接三.2.第二段代码
#程序中的对象
s1=OldboyStudent()
s2=OldboyStudent()
s3=OldboyStudent()

#如此,s1、s2、s3都一样了,而这三者除了相似的属性之外还各种不同的属性,这就用到了__init__
#注意:该方法是在对象产生之后才会执行,只用来为对象进行初始化操作,可以有任意代码,但一定不能有返回值
class OldboyStudent:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex

s1=OldboyStudent('李坦克','男',18) #先调用类产生空对象s1,然后调用OldboyStudent.__init__(s1,'李坦克','男',18)
s2=OldboyStudent('王大炮','女',38)
s3=OldboyStudent('牛榴弹','男',78)

#程序中对象的用法
#执行__init__,s1.name='牛榴弹',很明显也会产生对象的名称空间
s2.__dict__
{'name': '王大炮', 'age': '女', 'sex': 38}

s2.name #s2.__dict__['name']
s2.name='王三炮' #s2.__dict__['name']='王三炮'
s2.course='python' #s2.__dict__['course']='python'
del s2.course #s2.__dict__.pop('course')

#d1.a()   调用a方法时自动把d1作为第一个位置参数传入

实例只有数据属性,调用类的函数属性
在这里插入图片描述
5.绑定到对象的方法的特殊之处

#改写
class OldboyStudent:
    school='oldboy'
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
    def learn(self):
        print('%s is learning' %self.name) #新增self.name

    def eat(self):
        print('%s is eating' %self.name)

    def sleep(self):
        print('%s is sleeping' %self.name)


s1=OldboyStudent('李坦克','男',18)
s2=OldboyStudent('王大炮','女',38)
s3=OldboyStudent('牛榴弹','男',78)

OldboyStudent.learn(s1) #李坦克 is learning
OldboyStudent.learn(s2) #王大炮 is learning
OldboyStudent.learn(s3) #牛榴弹 is learning

s1.learn() #等同于OldboyStudent.learn(s1)
s2.learn() #等同于OldboyStudent.learn(s2)
s3.learn() #等同于OldboyStudent.learn(s3)

类中定义的函数(未被装饰器装饰的)是类的函数属性,类可以使用,但有几个参数需要传几个参数

类中定义的函数(没有被任何装饰器装饰的),其实主要是给对象使用的,而且是绑定到对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法

强调:绑定到对象的方法的特殊之处在于,绑定给谁就由谁来调用,谁来调用,就会将‘谁’本身当做第一个参数self传给方法,即自动传值(方法__init__也是一样的道理)

绑定到对象的方法的这种自动传值的特征,决定了在类中定义的函数都要默认写一个参数self,self可以是任意名字,但是约定俗成地写出self。

6.属性的增删查改

class chinese:
    country='China'
    def __init__(name):
        self.name=name
    def play_ball(ball):    
        print('%s正在打%s'%(self.name,ball))
查:p1='alex'
       print(p1.name)
       print(p1.__dict__)
       print(p1.play_ball)   #绑定的chinese的属性
改:p1.country='CHINA'
删:del p1.country
增:p1.age=18

7.对象间交互

class Garen:        #定义英雄盖伦的类,不同的玩家可以用它实例出自己英雄;
    camp='Demacia'  #所有玩家的英雄(盖伦)的阵营都是Demacia;
    def __init__(self,nickname,aggressivity=58,life_value=455): #英雄的初始攻击力58...;
        self.nickname=nickname  #为自己的盖伦起个别名;
        self.aggressivity=aggressivity #英雄都有自己的攻击力;
        self.life_value=life_value #英雄都有自己的生命值;
    def attack(self,enemy):   #普通攻击技能,enemy是敌人;
        enemy.life_value-=self.aggressivity #根据自己的攻击力,攻击敌人就减掉敌人的生命值。
        
class Riven:
    camp='Noxus'  #所有玩家的英雄(锐雯)的阵营都是Noxus;
    def __init__(self,nickname,aggressivity=54,life_value=414): #英雄的初始攻击力54;
        self.nickname=nickname  #为自己的锐雯起个别名;
        self.aggressivity=aggressivity #英雄都有自己的攻击力;
        self.life_value=life_value #英雄都有自己的生命值;
    def attack(self,enemy):   #普通攻击技能,enemy是敌人;
        enemy.life_value-=self.aggressivity #根据自己的攻击力,攻击敌人就减掉敌人的生命值。
 
实例出二者:        
g1=Garen('草丛伦')
r1=Riven('锐雯雯')

交互:
g1.life_value
#455
r1.attack(g1)
g1.life_value
#401 

garen_hero.Q()称为向garen_hero这个对象发送了一条消息,让他去执行Q这个功能,类似的有:
garen_hero.W()
garen_hero.E()
garen_hero.R()

五.python中关于OOP的常用术语
1.抽象/实现:
抽象指对现实世界问题和实体的本质表现,行为和特征建模,建立一个相关的子集,可以用于绘程序结构,从而实现这种模型。抽象不仅包括这种模型的数据属性,还定义了这些数据的接口。

对某种抽象的实现是对此数据及与之相关接口的现实化(realization)。现实化这个过程对客户程序应当是透明且无关的

2.封装/接口:封装描述了对数据/信息进行隐藏的观念,它对数据属性提供接口和访问函数。通过任何客户端直接对数据的访问,无视接口,与封装性都是背道而驰的,除非程序员允许这些操作。作为实现的 一部分,客户端根本就不需要知道在封装之后,数据属性是如何组织的。在Python中,所有的类属性都是公开的,但名字可能被“混淆”了,以阻止未经授权的访问,但仅此而已,再没有其他预防措施了。这就需要在设计时,对数据提供相应的接口,以免客户程序通过不规范的操作来存取封装的数据属性。

注意:封装绝不是等于“把不想让别人看到、以后可能修改的东西用private隐藏起来”

真正的封装是,经过深入的思考,做出良好的抽象,给出“完整且最小”的接口,并使得内部细节可以对外透明

(注意:对外透明的意思是,外部调用者可以顺利的得到自己想要的任何功能,完全意识不到内部细节的存在)

3.合成:合成扩充了对类的 述,使得多个不同的类合成为一个大的类,来解决现实问题。合成 述了 一个异常复杂的系统,比如一个类由其它类组成,更小的组件也可能是其它的类,数据属性及行为, 所有这些合在一起,彼此是“有一个”的关系。

4.派生/继承/继承结构:
派生描述子类衍生出新特性,新类保留已存类类型中所有需要的数据/行为,但允许修改/其它自定义操作,都不会修改原类的定义
继承描述了子类属性从祖先类继承这样一种方式
继承结构表示多“代”派生,可以述成一个“族谱”,连续的子类,与祖先类都有关系。

5.泛化/特化:
基于继承
泛化表示所有子类与其父类及祖先类有一样的特点。
特化描述所有子类的自定义(什么属性让它与其祖先类不同)

6.多态与多态性:多态指的是同一种事物的多种状态,水这种事物有多种不同的状态—冰,水蒸气

多态性的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的类。

冰,水蒸气,都继承于水,它们都有一个同名的方法就是变成云,但是冰.变云(),与水蒸气.变云()是截然不同的过程,虽然调用的方法都一样

7.自省/反射:自省也称作反射,这个性质展示了某对象是如何在运行期取得自身信息的。如果传一个对象给你,你可以查出它有什么能力,这是一项强大的特性。如果Python不支持某种形式的自省功能,dir和type内建函数,将很难正常工作。还有那些特殊属性,像__dict__,name__及__doc

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值