封装,property装饰器,绑定方法
1、什么封装
封:属性对外是隐藏的,但对内是开放的
装:申请一个名称空间,往里装入一系列名字/属性
2、为什么要封装
封装数据属性的目的
首先定义属性的目的就是为了给类外部的使用使用的,
隐藏之后是为了不让外部使用直接使用,需要类内部开辟一个接口
然后让类外部的使用通过接口来间接地操作隐藏的属性。
精髓在于:我们可以在接口之上附加任意逻辑,从而严格控制使用者对属性的操作
封装函数属性
首先定义属性的目的就是为了给类外部的使用使用的,
隐藏函数属性是为了不让外不直接使用,需要类内部开辟一个接口
然后在接口内去调用隐藏的功能
精髓在于:隔离了复杂度
3、如何封装
# 如何隐藏:在属性前加上__开头
#1、 这种隐藏仅仅只是一种语法上的变形操作
#2、 这种语法上的变形只在类定义阶段发生一次,因为类体代码仅仅只在类定义阶段检测一次
#3、 这种隐藏是对外不对内的,即在类的内部可以直接访问,而在类的外则无法直接访问,原因是
# 在类定义阶段,类体内代码统一发生了一次变形
#4、 如果不想让子类的方法覆盖父类的,可以将该方法名前加一个__开头
# class People: # __country="China" # def __init__(self,name,age,sex): # self.__name=name # self.age=age # self.sex=sex # # def eat(self): # print("eat....") # print(People.__country)#凡是在内部进行查看都时没问题, # print(self.__name) #因为在定义的时候都一起发生改变 # # # obj=People("yf",18,"mael") # obj.eat() #这是访问类内部的函数名,所以内部访问可以看见 # print(People.__country)#当类的数据变量名被隐藏的时候直接去访问时看不见 # print(People.eat(123)) #当类去访问其内部的函数间接访问该内容的时候可以看得见 # print(obj.__name) # People.__x=11 #因为时在定义阶段进行封装发生一次,所以后面就算添加进去也只是其添加时形态__x # print(People.__dict__) #通过查看类的内部情况可以看见,所有被隐藏的属性,都 # 是在前面变化成 _类名 的形式 # print(People._People__country)#按照其封装方式 _类名__属性 的形式时可以查到 # peo1=People("egon",18,"male") # print(peo1.__dict__) # peo1.__111 # print(peo1.__dict__)
# class Foo: # def __f1(self): # print("Foo.f1") # # def f2(self): # print("Foo.f2") # self.__f1() # # class Bar(Foo): # def __f1(self): # print("Bar.f1") # # obj=Bar() # obj.f2() #在没隐藏的状态下显示Foo.f2 Bar.f1 #如果隐藏子类 #显示 Foo.f2 Foo.f1
# class People: # def __init__(self,name,age): # self.__name=name # self.__age=age # # def tell_info(self): # print("%s:%s" %(self.__name,self.__age)) # # self.set_info(self.__name,self.__age) # def set_info(self,name,age): # if type(name) is not str: # raise TypeError("用户名必须为str类型")#主动报错方式,是自己设置的内容, # # 代替了print与return # if type(age) is not int: # raise TypeError("年龄必须为int类型") # # self.__name=name#当逻辑判断都通过的时候就可以更改属性 # self.__age=age # peo1=People("egon",18) # peo1.tell_info() # #首先定义属性的目的就是为了给类外部的使用使用的 # # 隐藏之后是为了不让外部使用直接使用,需要类内部开辟一个接口 # #然后让类外部的使用通过接口来间接的操作是隐藏的属性 # #精髓在于:我们可以再接口上附加任意逻辑,从而严格控制使用者对属性的操作 # peo1.set_info("egon",19) # peo1.tell_info() # peo1.__name=123#经过隐藏后,要像直接修改属性的姓名密码,都是不会成功, # 除非是按照改变方式作死的去改 # peo1.age=22 # peo1.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()
property装饰器用于将被装饰的方法伪装成一个数据属性,在使用时可以不用加括号而直接引用
# class People: # def __init__(self,name,weight,height): # self.name=name # self.weight=weight # self.height=height # @property #对象使用属性,可以变为不需要括号就直接进行使用 # def bmi(self): # return self.weight / (self.height ** 2) # # peo1=People("yf",68,1.75) # print(peo1.bmi) # peo1.height=1.85#通过更改身高,对象使用bmi属性可以达到改变的效果,表示property装饰器有效 # print(peo1.bmi)
# class People: # def __init__(self,name): # self.name=name # # @property # def name(self): # return "<名字是:%s>" %self.__name # # @name.setter # def name(self,name): # if type(name) is not str: # raise TypeError("名字必须是str类型") # self.__name=name # # @name.deleter # def name(self): # del self.__name # # peo1=People("egon") # print(peo1.name) # # peo1.name="Egon"#会自动判断进行的是修改或者查看,删除 # print(peo1.name) # # del peo1.name # print(peo1.name)#如果没有property那么这些操作都是先按照最后一个对象属性的这个名字操作(这样就乱了)
1、绑定方法
特性:绑定给谁就应该由谁来调用,谁来调用就会讲谁当作第一个参数自动传入
《精髓在于自动传值》
绑定方法分为两类
1.1绑定给对象方法
在类内部定义的函数(没有被任何装饰器修改的),默认就是绑定给对象用的
1.2绑定给类的方法
在类内部定义的函数如果被装饰器@classmethod装饰,
那么则是绑定给累的,应该由类来调用,类来调用就自动将类当作第一个参数自动传入
2、非绑定方法
类中定义的函数如果被装饰器@staticmethodz装饰,那么该函数就变成非绑定方法
既不与类绑定,又不与对象绑定,意味着类与对象都可以来调用
但是无论谁来调用,都没有任何自动传值的效果,就是一个普通函数
3、应用
如果函数体代码需要用外部传入的类,则应该将该函数定义成绑定给类的方法
如果函数体代码需要用外部传入的对象,则应该将该函数定义成绑定给对象的方法
如果函数体代码及不需要外部传入的类也不需要外部传入的对象,则应该将该函数定义成非绑定方法/普通函数
# class Foo: # @classmethod#绑定给类 # def f1(cls):#绑定给类的函数,内部会自动传入cls代表是跟类绑定 # print(cls) # def f2(self): # print(self) # obj=Foo() # print(obj.f2) # print(Foo.f1) # #1、f1绑定给类的 # #了解:绑定给类的应该由类来调用,但对象其实也可以使用,只不过自动传入的任然是类 # print(Foo.f1)#<bound method Foo.f1 of <class '__main__.Foo'>> # print(obj.f1)#<bound method Foo.f1 of <class '__main__.Foo'>>#对象调用类的绑定可以不用传值,但是类还是会自动传入,# 显示绑定给类的 # Foo.f1()#<class '__main__.Foo'> # obj.f1()#<class '__main__.Foo'> #2、f2是绑定给对象的 # obj.f2() # Foo.f2(obj)
# import settings # import uuid # class Mysql: # def __init__(self,ip,port): # self.uid=self.create_uid() # self.ip=ip # self.port=port # # def tell_info(self): # print("%s:%s" %(self.ip,self.port)) # #类获取到外部导入的IP与PORT后直接通过对象进行查看 # @classmethod # def from_conf(cls): # return cls(settings.IP,settings.PORT) # #因为是从外部导入内容,所以需要通过类导入进行获取 # @staticmethod#加上这个装饰器后变成普通函数 # def func():#这里面有几个形参,对象或者类调用就必须要有几个实参 # print("不与任何人绑定") # @staticmethod # def create_uid():#自动生成id,uuid生成的id每次都不一样 # return uuid.uuid1() # # # 默认的实例化方式:类名(..) # obj=Mysql('10.10.0.9',3307) # # 一种新的实例化方式:从配置文件中读取配置完成实例化 # # ojb=Mysql.from_conf()#类的传值基本都用于配置文件的读取 # # ojb.tell_info() # # #普通函数都可以直接使用,只是有几个参数就传几个,没有自动传入 # # obj.func() # # Mysql.func() # # print(obj.func) # # print(Mysql.func) # # print(obj.uid)#是调用对象内的id属性看id