python 封装、绑定

python 封装、绑定

image

面向对象共有三大特性:封装、继承、多态,这篇文章主要介绍一下类的封装,以及类的绑定。

  • 封装:意思就是整合代码,将数据和功能整合到一起,起到规范代码的作用,更好的组织了项目的整体结构,减少了代码的冗余度,提升了可扩展性

  • 类的封装主要分为数据封装、方法封装

1、数据、方法的封装

示例如下:

# 未封装前
name = 'HammerZe'
age = 18
gender = 'male'
print(f' name:{name}\n',
    f'age{age}\n',
      f'gender:{gender}')
# 结果
name:HammerZe
 age:18
 gender:male

这样看起来数据完全暴露出来,一个人的信息如此,如果多人的信息这样统计起来或者修改起来岂不是很麻烦?规定每个人的信息格式,整合起来首先比较方便,只需几行代码就可以显示一个人的全部信息

# 数据、方法封装
class Self_Info():
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender


# 方法/功能的封装
    def view_info(self):
        print(f" name:{self.name}\n",
              f"age:{self.age}\n",
              f"gender:{self.gender}")

obj = Self_Info('HammerZe',18,'male')
obj.view_info()
# 结果
 name:HammerZe
 age:18
 gender:male

看似封装后的代码多了很多,如果下次需要格式化输出其他人的信息,只需按照格式传参即可,不需要重新定义,重新写,这就规范了数据的一致性,比较好管理,上篇中介绍到类的增删查改,这样个人信息是不是也更好修改,不需要“牵一发而动全身”

2、隐藏属性

Python中,类内的属性是可以私有化的,就是把数据隐藏起来,对类外隐藏,类内可继续操作数据或方法。

  • 私有属性格式:__属性名(数据/方法)

示例如下:

'''未私有前'''
class Info():
    address = 'shanghai'

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


    def get_info(self):
        print(f" name:{self.name}\n",
                       f"age:{self.age}\n",
                       f"gender:{self.gender}\n",
                       f"address:{self.address}")

obj = Info('HammerZe',18,'male')
obj.get_info()
# 结果
 name:HammerZe
 age:18
 gender:male
 address:shanghai
    
    
'''如果我们不想对类外开放个人地址,可以私有化'''
# 属性私有
__address = 'shanghai' # 只需在数据名前加__,如果继续获取会报错
# 结果>>>AttributeError: 'Info' object has no attribute 'address'



# 属性私有
class Info():
    __address = 'shanghai' #底层:_Info__address

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


    def get_info(self):
        print(f" name:{self.name}\n",
                       f"age:{self.age}\n",
                       f"gender:{self.gender}\n",
                       f"address:{self.__address}") 
        
'''获取地址前加__'''
obj = Info('HammerZe',18,'male')
obj.get_info()
# 结果
 name:HammerZe
 age:18
 gender:male
 address:shanghai
    
'''私有了就不能获取到了吗?这个只是更改了数据的格式,如果想访问到可以通过获取数据格式,访问'''
# 通过__dict__查看名称空间,发现__address格式变为_Info__address
print(Info.__dict__)
# 所以在类外通过获取到数据格式也是可以获取到的
print(obj._Info__address) # shanghai
  • 注意!数据和方法的私有格式一样,知道底层的格式也是可以调用/获取到方法内和数据的信息,互联网没有绝对的安全

3、开放接口

  • 类的私有原则是对外私有,但是在类内可以修改和继续使用,定义属性就是为了使用

计算器有很多种功能,但是我们在使用的时候只需输入数,通过计算就能得到结果,那么底层的数学原理我们不需要知道,只求结果即可,那么很多功能就可以私有,只暴露给用户结果!

# 属性私有
'''以幂运算为例'''
class Calculator():
    def __init__(self):
        self.num1 = int(input('请输入底数>>>:'))
        print(f'您输入的底数为{self.num1}')

    # 幂运算
    def __pow(self,n):
       return self.num1**n

    def get_res(self):
        get_n = int(input('输入要计算幂运算的次方数>>>:'))
        res = self.__pow(get_n)
        print(f'幂运算结果为{res}')

num = Calculator()
num.get_res()

# 结果
请输入底数>>>:22
您输入的底数为22
输入要计算幂运算的次方数>>>:1
幂运算结果为22

'''这样我们只需要输入数,幂运算的功能可以隐藏起来,只暴露给用户功能接口,返回结果'''

4、绑定方法

1、对象的绑定

class Info():
    def __init__(self,name,age):
        self.name = name
        self.age = age
        
'''绑定给对象的方法,对象来调用,会把自己当成第一个参数传到函数里面self'''

    def get_info(self):
        print(f'姓名:{self.name}|年龄:{self.age}')

obj = Info('HammerZe',18)
obj.get_info()
# 结果
# 姓名:HammerZe|年龄:18

2、类的绑定(classmethod)

class Student():
    def __init__(self,name,age):
        self.name = name
        self.age = age

    # @classmethod
    # def change(self):
    #     obj = self.__class__('HammerZe', 18)
    #     return obj
    '''上面注释掉这种方法等价与下面的方法'''
    @classmethod
    def change(cls):
        obj = cls('HammerZe', 18)
        return obj

# 生成对象
stu = Student('ly',18)
print(stu.__dict__)
print(id(stu))

# 生成对象,再用新对象接收
obj = stu.change()
print(obj.__dict__)
print(id(obj))

'''stu.__class__.fun() 等价 Student.fun()'''
# 类调用
obj1 = Student.change()
print(obj1.__dict__)
print(id(obj1))

3、非绑定方法(staticmethod)

''' 统计调用类,产生了多少个对象'''


class Count_obj():
    # 定义count
    count = 0

    def __init__(self):
        Count_obj.count += 1

    # 静态方法/非绑定方法:既不绑定给类,也不绑定给对象
    @staticmethod
    def count_info():
        print(f'产生了{Count_obj.count}个对象')


# 调用类产生对象
obj = Count_obj()
obj1 = Count_obj()
obj2 = Count_obj()
obj3 = Count_obj()
obj4 = Count_obj()
# 查看生成了多少个对象
Count_obj.count_info()

'''注意,修改的是类体内的count,如果self.count只能在对象的名称空间中产生一次count,达不到统计的效果'''

5、property()函数及property装饰器

1、 property() 函数

格式:property(fget=None, fset=None, fdel=None, doc=None)

  • fget:获取属性值的方法
  • fset:设置属性值的方法
  • fdel:删除属性值的方法
  • doc:属性描述信息

示例如下:

class Student:
    def __init__(self):
        self._age = None

    def get_age(self):
        print('查看属性时执行的代码')
        return self._age

    def set_age(self, age):
        print('设置属性时执行的代码')
        self._age = age

    def del_age(self):
        print('删除属性时执行的代码')
        del self._age

    age = property(get_age, set_age, del_age, '学生年龄')


student = Student()
# 注意要用 类名.属性.__doc__ 的形式查看属性的文档字符串
print('查看属性的文档字符串:' + Student.age.__doc__)
"""
查看属性的文档字符串:学生年龄
"""

# 设置属性
student.age = 18
"""
设置属性时执行的代码
"""

# 查看属性
print('学生年龄为:' + str(student.age))
"""
获取属性时执行的代码
学生年龄为:18
"""

# 删除属性
del student.age
"""
删除属性时执行的代码
"""
print(student.__dict__) # {}

2、property装饰器

property装饰器可以将类中的函数‘伪装成’对象的数据属性,对象在访问该特殊属性时会触发功能的执行,然后将返回值作为本次的访问结果

  • @property 语法糖提供了比 property() 函数更简洁直观的写法。

  • @property 装饰的方法是获取属性值的方法,被装饰方法的名字会被用做 属性名

  • @属性名.setter 装饰的方法是设置属性值的方法

  • @属性名.deleter 装饰的方法是删除属性值的方法

以下示例代码与使用 property() 函数版本的代码等价:

class Student:
    def __init__(self):
        self._age = None

    @property
    def age(self):
        print('查看属性时执行的代码')
        return self._age

    @age.setter
    def age(self, age):
        print('设置属性时执行的代码')
        self._age = age

    @age.deleter
    def age(self):
        print('删除属性时执行的代码')
        del self._age


student = Student()

# 设置属性
student.age = 18
"""
设置属性时执行的代码
"""

# 获取属性
print('学生年龄为:' + str(student.age))
"""
查看属性时执行的代码
学生年龄为:18
"""

# 删除属性
del student.age
"""
删除属性时执行的代码
"""

3、注意!

  • 可以省略设置属性值的方法,此时该属性变成只读属性。如果此时仍然设置属性,会抛出异常 AttributeError: can't set attribute。
  • 如果报错 RecursionError: maximum recursion depth exceeded while calling a Python object,很可能是对象属性名和 @property 装饰的方法名重名了,一般会在对象属性名前加一个下划线 _ 避免重名,并且表明这是一个受保护的属性。

property参考:


【待续】
image

posted @ 2021-12-03 18:16  HammerZe  阅读(140)  评论(0编辑  收藏  举报