python 3.1 对象的的特性 -- 封装

一 、属性隐藏

封装 : 数据和功能组成到一起就是封装

一 隐藏数据属性

class Stu_date():

    def __init__(self,x,y,z):
        self.name = x
        self.age = y
        self.__gender = z  # 变形stu_obj1._Stu_date__gender
    school = 'old'
    def __choose(self): # 变形 _Stu_date__choose
        print('%s 选课。。。'%self.name)


stu_obj1 = Stu_date('zhangsan',19,'male')
stu_obj1.choose()


在类或者对象的属性前添加 __shuxing  ,该属性就是被隐藏了。无法通过对象名和类名访问,



class Stu_date():

    def __init__(self,x,y,z):
        self.__name = x
        self.__age = y
        self.__gender = z  # 变形stu_obj1._Stu_date__gender
    school = 'old'
    def __choose(self): # 变形 _Stu_date__choose
        print('%s 选课。。。'%self.name)

    def get_info(self):
        print(self.__name,self.__age,self.__gender) #也变成了stu_obj1._Stu_date__gender


stu_obj1 = Stu_date('zhangsan',19,'male')
stu_obj1.get_info()

通过代码可以看到该隐藏是 对外不对内的,对外隐藏,对内不隐藏

为什么能对外不对内的???其实是在定义阶段,扫描语法 名字发生了变形,把__name ,变成了 obj1.Stu_date_name  了





作用是 在继承中,父类如果不想让子类覆盖自己的方法,可以使用这个方法进行定义为私有方法


隐藏属性的核心意义是什么??

1、把属性隐藏起来的意义,在类内开放接口,让外来使用者通过接口来操作属性值,我们可以在接口上附加任意的判断,来严格控制使用者对对象属性值的操作。

class Stu_Date():

    def __init__(self,x,y,z):
        self.__name = x
        self.__age = y
        self.__gender =z

    __school = 'old'

    def set_school(self,school_name):
        if type(school_name) is str:
            Stu_Date.__school = school_name
        else:
            print('必须是str')



    def set_info(self,name,age,gender='male'):
        if type(name) is str:
            self.__name=name
        else:
            print('必须是str')
        if type(age) is int:
            self.__age=age
        else:
            print('必须是int')
        if type(gender) is str:
            self.__gender=gender
        else:
            print('必须是str')

    def get_info(self):
        print(self.__school,self.__name,self.__age,self.__gender)

stu_obj1 = Stu_Date('zhangsan',19,'male')



stu_obj1.set_info(name='lisi',age=20)
stu_obj1.set_school('panggezhuang')
stu_obj1.get_info()

把函数属性隐藏起来,是为了隔离复杂度
 实际意义 :在类中,一个功能是有N个函数对象组成,那么诸多的小功能是为了完成大功能而存在,所以小功能函数就不在提供给外来者调用


打个比方,电脑开机,按下开机键,一直到桌面出现,类似于 一个开机键封装了N个小功能,而外来者只需要按下开机键即可。这个就是隔离复杂度的体现。

二 属性值变更控制 装饰器 perperty 类函数转变成类属性的功能


装饰器  property
把类函数转变成类属性的功能,比如


class people():

    def __init__(self,name,weight,height):
        self.name=name
        self.weight = weight
        self.heigh=height
    @property
    def bmi(self): # people1.bmi 看代码是一个对象的属性,但是加()却是一个功能,
                     # 而这个对象属性为了不让外来人迷惑,所以必须变形成属性的样子
                     # 加上1
        return self.weight /(self.heigh **2)


people1= people('张三',184,90)


# print(people1.bmi())
print(people1.bmi)



把类属性 通常在实例化对象后,生成了对象属性和类属性,通过加下划线 __name  __school 的方法,变形隐藏起来,仅提供给类内函数的使用。
问题1 通过perpenty可以把类函数变形成类的属性,如何防止修改和删除,变形后的类属性本质还是函数,是不能够删除的
问题2 类属性和函数属性必须要给外界使用,如何防止被调用者随意赋值、和 删除?




问题1 解答

class StuDate():

    def __init__(self,name,age):
        self.name = name
        self.__age =age

    def get_age(self):

        return self.__age *12

    def set_age(self,x):
        print('not set')

    def del_age(self):

        print('not del')

    age = property(get_age,set_age,del_age)


stu_obj1=StuDate('zhangsan',10)

print(stu_obj1.age)

stu_obj1.age =12
del stu_obj1.age

问题2 解答
class StuDate():

    def __init__(self,name,age):
        self.name = name
        self.__age =age

    def get_age(self):    # 查看对象的属性
        return self.__age

    def set_age(self,x):   # 修改对象的属性,类型已认证,修改成功
        if type(x) is not int:
            print('不能修改')
            return
        else:
            self.__age=x

    def del_age(self): # 删除对象的属性,不允许删除,失败
        print('不能删除')
    age = property(get_age,set_age,del_age)

stu_obj1 = StuDate('zhangsan',18)

print(stu_obj1.age)  # 查看对象的属性

stu_obj1.age =10   # 修改对象的属性,类型已认证,修改成功
stu_obj1.age ='111'  # 修改对象的属性,类型认证错误,修改失败
print(stu_obj1.age)

del stu_obj1.age  # 删除对象的属性,不允许删除,失败


 通过上面的perperty装饰器,可以加强调用者对(类和对象)属性值的操作的控制,相当于对属性值的操作加上了一道筛子,只有经过确认的命令才能执行,
 让对象属性和类属性不能够随意变更

升级 perpenty
在 在被装饰的"属性对象"上 添加功能


class StuDate():

    def __init__(self,name,age):
        self.name = name
        self.__age =age

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self,x):
        if type(x) is not int:
            print('不能修改')
            return
        else:
            self.__age=x
    @age.deleter
    def age(self):
        print('不能删除')

stu_obj1 = StuDate('zhangsan',18)
print(stu_obj1.age)  # 查看对象的属性
stu_obj1.age =10   # 修改对象的属性,类型已认证,修改成功
stu_obj1.age ='111'  # 修改对象的属性,类型认证错误,修改失败
print(stu_obj1.age)
del stu_obj1.age  # 删除对象的属性,不允许删除,失败

三 绑定方法 + 装饰器 classmethod staticmethod

绑定方法的总结
1、默认是绑定是给对象,自动传对象名

2、指定绑定给类的,自动传类名 classmethod

3、非绑定方法,不绑定对象和类名 不自动传任何参数, staticmethod

class foo():

    def __init__(self,x):
        self.f = x

    def f1(self):
        pass
    # <bound method foo.f1 of <__main__.foo object at 0x00000246E5CBA6A0>>
    绑定给一个对象的方法,自动传对象名
    @classmethod
    def f2(cls):
        pass

    <bound method foo.f2 of <class '__main__.foo'>>
    绑定给一个类的的方法 ,自动传类名

    @staticmethod
    def f3(x,y):
        pass
    <function foo.f3 at 0x00000267270AF1E0>
    <function foo.f3 at 0x00000267270AF1E0>
    是一个函数,谁都可以调用,不传任何形参


a1= foo(2)

print(a1.f1)
<bound method foo.f1 of <__main__.foo object at 0x00000246E5CBA6A0>>
绑定给一个对象的方法,
print(foo.f2)
<bound method foo.f2 of <class '__main__.foo'>>
绑定给一个类的的方法

print(foo.f3)
print(a1.f3)
<function foo.f3 at 0x00000267270AF1E0>
<function foo.f3 at 0x00000267270AF1E0>
是一个函数,谁都可以调用


通过上面的方法3种不同的绑定方法,扩大了函数的使用宽度。根据函数体代码的需求来选择的
可以根据业务场景划分不同的使用方法,
比如 在一个登陆mysql数据库实例中,
1、创建每一个调用者使用的事件ID,该场景不需要使用到对象名和类名,就可以使用 非绑定方法
2、创建一个自定义的对象属性,需要传入事件ID,IP,端口号,可以使用 默认绑定方法,因为要使用对象名
3、创建一个默认对象属性,需要从配置文件中读取,创建对象不需要传值,就是可以使用类的绑定方法,在类的内部自定完成对象属性的加载。





import uuid
import setting

class Mysql_Init():

    def __init__(self,IP,PORT):
        self.mid =self.creat() #2、非绑定方法 都可以调用,那么使用对象产生uuid
        self.__ip = IP
        self.__port = PORT

    def get_info(self):
        print('%s:<%s %s>'%(self.mid,self.__ip,self.__port))

    # 4、创建一个自定义的对象属性,IP,端口号,可以使用 默认绑定方法,因为要使用对象名

    @staticmethod
    def creat():
        return uuid.uuid4()

    # 1、创建每一个调用者使用的事件ID,该场景不需要使用到对象名和类名,就可以使用 非绑定方法

    @classmethod
    def from_conf(cls):
        return cls(setting.IP,setting.PORT)  # 6、创建一个默认对象属性,需要从配置文件中读取,创建对象不需要传值,就是可以使用类的绑定方法,在类的内部自定完成对象属性的加载。



obj1 = Mysql_Init('127.0.0.1',3306) # 3、创建一个自定义的对象属性,IP,端口号,可以使用 默认绑定方法,因为要使用对象名

obj2= Mysql_Init.from_conf()  # 5、创建一个默认对象属性,需要从配置文件中读取,创建对象不需要传值,就是可以使用类的绑定方法,在类的内部自定完成对象属性的加载。

obj1.get_info()



obj1 = Mysql_Init('127.0.0.1',3306)
# 根据这个代码可以看出 对象实例化需要的是  cls(func1,func2) , 使用classmethod配合函数返回, 默认配置的对象实例化。


总结: 函数体代码需要类名的,绑定给类
总结: 函数体代码需要对象名的,绑定给对象
总结: 函数体代码都不需要的,不绑定

一 定义一个people类,每个人的对象都有 名字 年龄 性别三个属性,分别完成对这3个属性的隐藏,开放接口,property伪装操作
 在开放接口里严格控制赋值操作的数据类型问题

二 定义MySQL类
   1、对象有ID,host,port 三个属性
   3、定义工具creat_id ,在实例化时,为每个对象随机生成ID,保证id唯一
   4、为对象定制方法,save和get_obj_by_id,save 能自动将对象序列化到文件中,文件的路径为配置文件中的DB_PATH ,文件名为id号,
   保存之前,验证对象对象是否已存在,若存在抛异常; get_obj_by_id 方法用来从文件中反序列化出对象。
posted @ 2022-01-28 17:38  mmszxc  阅读(56)  评论(0)    收藏  举报