类,封装
2021-09-06 本文已影响0人
慕知
一,面向对象
面向过程:
优点:复杂的过程流程化,简单化
缺点:可扩展性差
举例:linux中shell编译安装,这种固定的流程,面向过程方式堆积命令即可
面向对象:对象是特征与技能的合体
优点:可扩展性强
缺点:编程复杂难度高,容易出现过度设计
二,类
1,类的定义
在程序中:一定先有类再有对象
对象可以访问到类中共有的数据与功能,所以类中的内容仍然是属于对象的;
类只不过是一种节省空间、减少代码冗余的机制,面向对象编程最终的核心仍然是去使用对象。
class Student:
beauty='mzz'
def learning(self):
print('mzz like to study')
print('----->running')
# ----->running会在"类"定义阶段执行
# 查看类的内存空间
print(Student.__dict__)
# {'__module__': '__main__', 'beauty': 'mzz', 'learning': <function Student.learning at 0x102c43e50>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}
print(Student.beauty) # 数据类型(变量)
# mzz
print(Student.learning) # 函数属性
# <function Student.learning at 0x103063e50>
# 类调用的learning是一个函数,所以要按照函数的调用方式,即要加上self这个参数
Student.learning('mzz')
# ----->running
# mzz like to study
# 增加
Student.counry="China"
print(Student.counry)
# China
# 修改
Student.beauty="Egg"
print(Student.beauty)
# Egg
# 删除
del Student.counry
#print(Student.counry) # 会报错,已经删除
# 查看类的内存空间验证,没有country
print(Student.__dict__)
2,类的实例化过程
# 调用类的过程又称实例化
#1)得到一个返回值,即对象,该对象是一个空对象
#2) Student.__init__,把类当作第一个参数传进去
class Student:
beauty='mzz'
def __init__(self,name,age,sex): # 在调用时自动触发
self.Name=name
self.Age=age
self.Sex=sex
def learning(self):
print('mzz like to study')
s1=Student('mz',13,'female')
print(s1.__dict__)
# {'Name': 'mz', 'Age': 13, 'Sex': 'female'}
print(s1.Sex)
# female
s2=Student('egon',22,'male')
print(s2.Name)
# egon
3,属性查找与绑定方法
#1, 查找一个对象的属性,先找自己的__dict__,再找类的__dict__
class Student:
school='Asia'
def __init__(self,name,age,sex): # 在调用时自动触发
self.Name=name
self.Age=age
self.Sex=sex
def learning(self):
print('%s like to study' %self.Name)
s1=Student('mz',13,'female')
s2=Student('egon',22,'male')
#2, 类的数据属性是所有对象共享,所有对象都指向同一个内存地址
print(Student.school,id(s1.school))
# Asia 4303951152
print(Student.school,id(s2.school))
# Asia 4303951152
Student.school='India'
print(Student.school,id(s1.school))
# India 4383185008
print(Student.school,id(s2.school))
# India 4383185008
#3, 类中定义的函数是绑定给对象使用,类也可以使用(用起来会很复杂)
# 不同对象就是不同绑定方法
# 绑定给谁,就应该给谁调用
print(Student.learning)
# <function Student.learning at 0x10029f790>
print(s1.learning)
# <bound method Student.learning of <__main__.Student object at 0x10025dfa0>>
print(s2.learning)
# <bound method Student.learning of <__main__.Student object at 0x1002962b0>>
s1.learning()
# mz like to study
s2.learning()
# egon like to study
# 接上验证,函数属性的绑定方法
class Student:
school='Asia'
def __init__(self,name,age,sex): # 在调用时自动触发
self.Name=name
self.Age=age
self.Sex=sex
def learning(self,x,y):
print('%s like to study' %self.Name)
print(x,y)
s1=Student('mz',13,'female')
s2=Student('egon',22,'male')
s1.learning(1,2)
# mz like to study
# 1 2
s2.learning(1,2)
# egon like to study
# 1 2
#类也可以使用(用起来会很复杂)
Student.learning(s1,1,2)
# mz like to study
# 1 2
类的函数属性调用默认必须要有一个参数
示例
# 示例:任何一个老师都可以看到老师的个数
class Teacher:
school='Asia'
count=0
def __init__(self,name,sex,age,level,salary):
self.name=name
self.age=age
self.level=level
self.salary=salary
Teacher.count+=1
def teach(self):
print('%s is teaching' %self.name)
t1=Teacher('小艾','male',20,1,3000)
t2=Teacher('小林','male',21,2,4000)
t3=Teacher('小狗','female',23,3,5000)
# 查看Teacher数量
print(t1.count)
print(t2.count)
print(t3.count)
# 3 都是3
4,类和对象
a1=list([1,2,3,4])
a2=list([1,2,3,4])
print(id(a1))
# 4343027392
print(id(a2))
# 4343027456
a1.append('a')
a2.append('b')
print(a1)
# [1, 2, 3, 4, 'a']
print(a2)
# [1, 2, 3, 4, 'b']
python中,一切皆对象,类型本质都是类。
数据类型都是一个个类创造的对象
-- 游戏中生命值攻击力示例
class Dog:
camp='A' #所有类里面的(xiaohua)都属于A组
def __init__(self,name,life_value=200,attack_value=10):
# 初始生命值200,初始攻击力10
self.name=name
# 赋值,前面的name可以任意起名
self.life_value=life_value
self.attack_value=attack_value
def attack(self,enemy): # 攻击技能
enemy.life_value-=self.attack_value
# 敌人的生命力=敌人的生命力减去自己的攻击力
class Cat:
camp='B'
def __init__(self,name,life_value=100,attack_value=80):
self.name=name
self.life_value=life_value
self.attack_value=attack_value
def attack(self,enemy):
enemy.life_value-=self.attack_value
xiaohua=Dog('xiaohua')
mimi=Cat('mimi')
print(xiaohua.life_value)
# 200
mimi.attack(xiaohua)
print(xiaohua.life_value)
# 120
5,代码级别看面向对象
# 伪代码示例
#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
三,封装
1,隐藏属性
# 正常情况下,x可以通过调用Foo正常访问
class Foo:
x=1
def __init__(self,name,age):
self.name=name
self.age=age
print(Foo.x)
# 1
abc=Foo('dog',3)
print(abc.age)
# 3
# 隐藏属性,在需要隐藏属性的前面加上__(两个下划线)
# 隐藏属性是在调用阶段生成,所以在类内部是可以直接访问双下滑线开头的属性的
class Foo:
__x=1
def __init__(self,name,age):
self.name=name
self.age=age
#print(Foo.x) 会报错
obj=Foo('mzz',18)
print(obj.name,obj.age)
# mzz 18
class Foo:
__x=1
def __init__(self,name,age):
self._name=name
self._age=age
print(Foo.__dict__)
# {'__module__': '__main__', '_Foo__x': 1, '__init__': <function Foo.__init__ at 0x10304f790>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
# 根据属性的变形,通过以下方式可以得到隐藏的属性的值
print(Foo._Foo__x)
# 1
2,为什么使用隐藏属性
class People:
def __init__(self,name):
self.__name=name
#People.get_name('mmm') # 会报错
obj=People('muzz')
#print(obj.set_name()) # 会报错
class People:
def __init__(self,name):
self.__name=name
def get_name(self):
# 类似于开个接口,可以赋予其他的东西
print('谁是小可爱')
print(self.__name)
obj=People('muzz')
obj.get_name()
# 谁是小可爱
# muzz
# 类的创造者,可以严格控制使用者对属性的操作
class People:
def __init__(self,name):
self.__name=name
def get_name(self):
# 类似于开个接口,可以赋予其他的东西
print('不给看')
obj=People('muzz')
obj.get_name()
# 不给看
# 如果更改名字,如下
class People:
def __init__(self,name):
self.__name=name
def get_name(self):
print(self.__name)
def set_name(self,val):
self.__name=val
obj=People('muzz')
obj.set_name('egon')
obj.get_name()
# egon
# 增加条件
class People:
def __init__(self,name):
self.__name=name
def get_name(self):
print(self.__name)
def set_name(self,val):
if type(val) is not str:
print('必须字符串类型')
return
self.__name=val
obj=People('muzz')
obj.set_name(12235)
obj.get_name()
# 必须字符串类型
# muzz
3,property装饰器
1)用法一
"""
成人的BMI数值:
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖, 高于32
体质指数(BMI)=体重(kg)÷身高^2(m)
EX:70kg÷(1.75×1.75)=22.86
"""
# 例1:传值给People,得到ami的值需要obj.ami(),即调用函数的方式
class People:
def __init__(self,name,weight,height):
self.name=name
self.weight=weight
self.height=height
def ami(self):
print(self.weight / (self.height **2))
obj=People('mzz',55,1.70)
print(obj.ami())
# 19.031141868512112
# None
obj.ami()
# 19.031141868512112
# 例2:加上@property 可以正常以obj.ami方式调用
class People:
def __init__(self,name,weight,height):
self.name=name
self.weight=weight
self.height=height
@property
def ami(self):
print(self.weight / (self.height **2))
obj=People('mz',55,1.70)
obj.ami
# 19.031141868512112
2)用法2
# property用法2
class People:
def __init__(self, name):
self.__name = name
def get_name(self): # obj1.name
return self.__name
def set_name(self, val): # obj1.name='EGON'
if type(val) is not str:
print('必须传入str类型')
return
self.__name = val
def del_name(self): # del obj1.name
del self.__name
name=property(get_name,set_name,del_name)
obj=People('eggg')
print(obj.name)
obj.set_name('EGGG')
print(obj.name)
#EGGG
obj.del_name()
print(obj.name) # 会报错
3) 用法3 (建议)
# property用法3
class People:
def __init__(self, name):
self.__name = name
@property
def name(self): # obj1.name
return self.__name
@name.setter
def name(self, val): # obj1.name='EGON'
if type(val) is not str:
print('必须传入str类型')
return
self.__name = val
@name.deleter
def name(self): # del obj1.name
print('不让删除')
obj=People('mz')
print(obj.name)
# mz
obj.name=12
# 必须传入str类型
obj.name='xiaohua'
print(obj.name)
# xiaohua
del obj.name
# 不让删除
注意def name的name要统一