面向过程与面向对象
面向过程:根据流程从上往下执行;面向对象:通过新建的对象(类)实现对应的功能
对象由属性和行为构成
使用类的优点:数据可重用、数据的权限设计
动态绑定
动态绑定指的是在类外部给类增加对应的属性或者方法;在下面这个案例中,给类增加方法使用的是lambda
函数
class sis:
pass # 定义一个空对象
lixin_sis = sis()
print(type(lixin_sis)) # 查看对象的属性<class sis>
# 动态绑定空对象的属性和行为
lixin_sis.name = '芙蓉姐姐'
# 使用匿名函数绑定行为
lixin_sis.kiss = lambda name : print(name,'kiss',lixin_sis.name)
print(lixin_sis.name)
lixin_sis.kiss('李欣')
在使用 python 的匿名函数 lambda
的时候,不能使用 self
指向对应类本身
重载运算符
重载运算符是在类中(对象中)对既定的运算符进行重新解释,比如我们想实现两个复数相加的功能
class complex:
def __init__(self, x, y):
self.x = x
self.y = y
def show(self, name):
print('%s 的值为:' % name ,self.x, '+', self.y, 'i')
# 重载:针对本类型,对 + 进行重新解释
def __add__(self, other):
# 使用匿名函数,用来接收并返回重载运算符的结果
return complex(self.x + other.x, self.y + other.y)
c1 = complex(4, 5)
c2 = complex(1, 2)
c1.show('c1')
c2.show('c2')
print(type(c1)) # 重载运算符,只对本类型器作用,所以在c1 c2 和 c3 的类型都是:<class:complex>
print(type(c2))
c3 = c1 + c2
# 等价于:c3 = c1.__add__(c2)
c3.show('c3')
print(type(c3))
重载运算符对 加法做了重新的解释,但是解释的作用仅仅局限于 complex 类中||
重载运算符的拓展类型
重载的拓展类型:比如我们需要针对对象中相同的运算符,设置不同的解释方法 时;可以采用重载的拓展,我们修改上述代码,让其实现复数与复数,复数与整数类型的运算
class complex:
def __init__(self, x, y):
self.x = x
self.y = y
def show(self, name):
print('%s 的值为:' % name, self.x, '+', self.y, 'i')
# 重载:针对本类型,对 + 进行重新解释
def __add__(self, other):
if type(other) == type(self): # 重载 complex + complex ,判断类型
return complex(self.x + other.x, self.y + other.y)
elif type(other) == type(10): # 重载 complex + int ,判断类型
return complex(self.x + other, self.y + other)
c1 = complex(4, 5)
c2 = complex(1, 2)
c3 = c1 + 10 # 进入 complex + int 类型语句
c3.show('c3')
c4 = c3 + c1 # 进入 complex + complex 类型语句
c4.show('c4')
通过 类型判断,根据不同的 other
参数类型判断__add__()
方法的返回,(两种方法的解释方式不同)从而实现了在一个重载方法内实现两种运算法则的共存
类的浅拷贝与深拷贝
对象的浅拷贝
对象的赋值是浅拷贝:即两个对象同指向一个内存空间;修改之前的 complex
类的代码,
class complex:
def __init__(self, x, y):
self.x = x
self.y = y
def show(self, name):
print('%s 的值是:' % name, self.x, '+', self.y, 'i')
c1 = complex(4,5)
c2 = c1
c1.show('c1')
c2.show('c2')
# 在这里可以发现,两个变量同时指向一个内存空间
print(id(c1),id(c2))
print(c1,c2)
c1.x = 100
c1.show('c1')
c2.show('c2')
可以发现两个变量同时指向一个内存空间(内存空间和 id 号相同),修改 c1
的值,变量 c2
的值也会发生改变
对象的深拷贝
可以通过调用类,进行复制变量的方法进行对象的深拷贝(不是通过 = 赋值);我们的上述浅拷贝代码的基础上,通过深拷贝的方法复制一个变量
# 进行类(对象)的深拷贝
c3 = complex(c1.x,c1.y)
print(c3,id(c3))
c1.x = 50
c1.show('c1')
c3.show('c3')
深拷贝复制的变量c3
不会和原变量c1
共享同一块内存空间,会在内存中开辟一块新的空间并复制变量c1
的值。我们打印对象的id号发现不一样,打印对象发现内存空间的地址不同,修改c1
不会对c3
造成任何影响
**总结:**类的浅拷贝(节省内存空间)类的深拷贝(保证数据的相对隔离安全;消耗内存较大)
函数的副本机制
这个案例展现了函数接收参数的工作原理
def changenum(num):
print('changenum',id(num))
num = 1000
print('changenum',id(num))
num = 50
changenum(num)
print(num)
print(id(num))
案例输出的结果为
changenum 140714334297280
changenum 2223319561264
50
140714334297280
列表作为参数的副本机制:
相当于在函数内新构建了一个变量,以上述案例为例;函数在接收到参数之后,在函数内部新建一个变量num
新建的变量与原函数的变量所占用的地址空间不一致;函数内部变量的变化不会对外部产生任何影响。也就是说:在函数开始的时候;只是拷贝了参数的地址,函数内修改变量的值,是新开辟了一个内存空间,不会对函数外的参数构成影响。
**针对列表的副本机制:**把列表当作参数;不会改变列表本来的地址,但是可以改变列表元素的内容:
def changelist(mylist):
print('beforechange',id(mylist)) # 复制参数地址,这里打印的地址与函数外参数列表的地址相同
mylist[1] = 20000
print('changelist',mylist) # 不可以修改参数列表的地址,打死你hi可以修改参数列表的内容(因为内容存放的位置是地址变量)
mylist = {1,2,3,4,5,6} # 改变类型,尝试修改列表地址|函数会新开辟一个内存空间,存放变量
print('finally change',id(mylist),mylist)
mylist = [1, 2, 3, 4, 5]
print(id(mylist), mylist)
changelist(mylist)
print(id(mylist), mylist)
这个案例的输出结果:
1885836415680 [1, 2, 3, 4, 5]
beforechange 1885836415680
changelist [1, 20000, 3, 4, 5]
finally change 1885836853984 {1, 2, 3, 4, 5, 6} # 注意在这里改变了列表参数类型,新开辟了一块内存空间
1885836415680 [1, 20000, 3, 4, 5]
对象作为参数的副本机制:
将对象作为参数传入函数的时候,函数会复制参数的地址,函数可以改变参数的属性内容但是函数不能改变参数的存储地址。
class data:
num = 100
def show(self):
print(self.num)
def change(datas):
print('change',id(datas))
datas.num = 19999 # 在这里改变了变量值,但是没有改变变量的内存空间
print('after change',id(datas))
d1 = data()
d1.show()
change(d1) # 将对象作为参数传入函数中;
d1.show()
副本机制:总结
函数的副本机制:函数的参数通常使用:浅拷贝的方法,即直接指向参数的地址;
不能从函数的内部修改:数字、字符串 的值:因为数字,字符串是一个固定的存储空间;
不能从函数内部修改:列表、对象所在的内存地址;但是可以修改原来数据包含的内容(也就是这两个数据集合的地址变量的内容)
私有变量
python 是一门解释型语言;比那辆存在解释为:引用;变量不存在解释为:动态绑定
私有变量不允许外部直接访问(一般会通过使用类内的方法访问)
私有变量的写法:__变量名 ; (在变量名前插入两个下划线)
class money:
def __init__(self):
self.__money = 100000
def show(self,name):
print('%s ,的月收入:'%name,self.__money)
ls = money()
# ls.__money = 999 # 这里属于动态绑定:因为无法访问内部的变量,只能绑定一个新的外部属性
print(ls.__money) # 打印的是外部属性的值;如果不绑定外部属性,就会报:AttributeError(没有属性)的错误
ls.show('ls') # 通过类内的函数进行访问;这里的访问结果是类中定义的 100000
私有变量的意义
私有变量的意义是给对应的比那辆设置访问权限;不是简单的通过外部就可以直接访问这个私有变量
案例:
class money:
def __init__(self):
self.__money = 100000
# 这是一个存钱的方法
def drop(self, num):
self.__money += num
print(self.__money)
# 这是一个取钱的方法
def deposit(self, passwd, num):
if passwd == "123456":
self.__money -= num
print(self.__money)
else:
print("密码错误")
# 这是一个查询存款的方法
def check(self, passwd):
if passwd == "123456":
print(self.__money)
else:
print("密码错误")
xiaoming = money()
xiaoming.drop(100000)
xiaoming.deposit("1234",2000)
xiaoming.deposit("123456",10000)
xiaoming.check("123456")
# print(xiaoming.__money) # 无法通过外部直接访问私有变量
在这个案例中,我们只可以通过调用对应类中函数的方法访问私有变量,无法从外部直接访问,从外部修改__money
的值会解释为动态绑定一个新的属性,不会修改私有变量本身的值
私有方法
私有方法:指的是不可以在外部直接访问的方法;**写法:__函数名(参数)😗*作用:进行函数(方法)隔离,给函数设置权限:
class money:
def __init__(self):
self.money = 100000
# 不可以外部访问
def __setmoney(self, num):
self.money = num
return self.money
# 不可以外部访问
def __getmoney(self):
return self.money
# 不可以外部访问
def __checkpass(self, passwd):
if passwd == '123456':
return True
else:
return False
# 可以外部访问(查询账户)
def checkAccount(self, passwd):
if self.__checkpass(passwd):
return self.__getmoney()
else:
return None
# 可以外部访问(修改账户)
def setAccout(self, passwd, num):
if self.__checkpass(passwd):
return self.__setmoney(num)
else:
return None
xiaoming = money()
print(dir(xiaoming))
print(xiaoming.checkAccount('123456'))
print(xiaoming.setAccout('123456',700000))
在这个案例中,我们打印 dir(xiaoming)
查看这个对象的所有函数就可以发现如下结果
['__class__', '__delattr__', '__dict__', '__dir__', ...'_money__checkpass','_money__getmoney','_money__setmoney', 'checkAccount', 'money', 'setAccout']
其中_money__getmoney
、_money__setmoney
、'_money__checkpass'
表示无法直接访问;在这个案例中,查询账户和修改账户的方法可以通过外部访问,调用:_money__checkpass
进行身份验证,如果返回值是true 则通过函数内部访问对应的私有方法;如果返回值是false 直接返回None
私有方法的意义:进行权限设计、加强数据隔离
私有变量、私有方法的本质
python 的私有变量和私有方法,是在函数外部给对应的变量或者方法前加上一个_类名
、哦我们可以对上述代码及西宁修改从而访问python的私有变量和私有方法:
# 访问私有方法|在__getmoney() 方法之前加上_money 的类名
print(xiaoming._money__getmoney())