ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

面向对象 - 封装,隐藏属性,property,绑定与非绑定方法,元类

2021-12-07 21:03:44  阅读:162  来源: 互联网

标签:__ .__ name People self 绑定 元类 print property


封装

  • 封装指的就是把数据与功能都整合到一起,听起来是不是很熟悉,没错,我们之前所说的”整合“二字其实就是封装的通俗说法。

  • 除此之外,针对封装到对象或者类中的属性,我们还可以严格控制对它们的访问,分两步实现:隐藏与开放接口.

隐藏属性

  • 在python中用双下划线开头的方式将属性隐藏起来(设置成私有的),但其实这仅仅只是一种变形操作

 类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__属性名的形式:
class People:
# 类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的 ↓
    __country = "China"  # 变形为 _People__country = "china"

    def __init__(self,name,age):
        self.__name = name  # self._People__name = name
        self.__age = age  # self._people__age = age

    def __func(self):  # _People__func
        print('xx')

    def tell_name(self):
        print(self.__name)  # self._People__name

"""
只有在类内部才可以通过__开头的形式访问到,如果目的是访问的话就不应该隐藏
# 类的数据属性
print(People._People__country)   # China 这么做没有意义
# 类的函数属性
print(People._People__func)  # <function People.__func at 0x7f9b008c7ee0>
"""

print(People.__dict__)
'''
{'__module__': '__main__', '_People__country': 'China', 
'__init__': <function People.__init__ at 0x7fcb5d1089d0>, 
'_People__func': <function People.__func at 0x7fcb5d1c7f70>, 
'tell_name': <function People.tell_name at 0x7fcb5d1c7ee0>, 
'__dict__': <attribute '__dict__' of 'People' objects>, 
'__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
'''

obj1 = People("jack", 18)
print(obj1.__dict__)  # {'_People__name': 'jack', '_People__age': 18}
"""
__开头的属性的特点:
    1、并没有真的藏起来,只是将属性名改了名字,变形了而已
    2、该变形只在类定义阶段、扫描语法的时候执行,此后__开头的属性都不会变形
    3、该隐藏对外不对内。对类外部确实有隐藏的效果,但对类内部没有隐藏
"""

# 此后__开头的属性都不会变形
obj1.__gender = "male"
# 打印结果可以发现并没有变形
print(obj1.__dict__)  # {'_People__name': 'jack', '_People__age': 18, '__gender': 'male'}
print(obj1.__gender )  # male 可以正常访问


# 该隐藏对外不对内
obj1.tell_name()  # jack

  

  • 封装的真谛在于明确地区分内外,封装的属性可以直接在内部使用,而不能被外部直接使用,然而定义属性的目的终归是要用,外部要想用类隐藏的属性,需要我们为其开辟接口,让外部能够间接地用到我们隐藏起来的属性,那这么做的意义何在???

1. 隐藏数据:将数据隐藏起来这不是目的。隐藏起来然后对外提供操作该数据的接口,然后我们可以在接口附加上对该数据操作的限制,以此完成对数据属性操作的严格控制。

  • 为何要隐藏属性?
  • 隐藏数据属性为了严格控制类外部访问者对属性的操作(保护数据)
# 示例:保护用户信息
class People:

    def __init__(self, name, age):
        self.__name = name  # self._People__name = name
        self.__age = age  # 将名字和年龄藏起来

    def tell_info(self):  # 访问不了就开接口
        print("<%s: %s>" % (self.__name, self.__age))

    def set_info(self, name, age):  # 严格控制外部对属性的操作
        if type(name) is not str:
            print("名字必须是字符串")
            return
        if type(age) is not int:
            print("年龄必须是数字")
            return
        self.__name = name
        self.__age = age


obj1 = People("jack", 18)
# 访问用户信息
obj1.tell_info()  # <jack: 18>

# 修改用户信息
obj1.set_info("tom", 28)
obj1.tell_info()  # <tom: 28>

obj1.set_info(12313, 28)  # 名字必须是字符串

obj1.tell_info()  # <tom: 18>  再查看会发现并未修改成功,做到了控制

  

2. 隐藏方法:目的是隔离复杂度

"""
取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,
很明显这么做隔离了复杂度,同时也提升了安全性。
"""
# 隔离复杂度的例子
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)

什么是特性property

property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值

例一:BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解)

  • 成人的BMI数值:

    • 过轻:低于18.5

    • 正常:18.5-23.9

    • 过重:24-27

    • 肥胖:28-32

    • 非常肥胖, 高于32

  体质指数(BMI)=体重(kg)÷身高^2(m)

  EX:70kg÷(1.75×1.75)=22.86

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)

p1 = People('jack', 75, 1.8)

# 正常访问p1.bmi()需要加括号
# print(p1.bmi())  # 23.148148148148145


# 用 @property 将bmi功能(函数属性)伪装成了数据属性
print(p1.bmi)  # 23.148148148148145

  

property的使用:

class People:
    def __init__(self,name):
        self.__name = name  # 隐藏名字属性
            

    @property  # 1.查看行为
    def name(self):  # 被property装饰的name函数,其实是__name被藏起来了
        return self.__name

    @name.setter  # 2.修改行为
    def name(self,x):  
        if type(x) is not str:
            raise Exception("名字必须是字符串类型")
        self.__name = x

    @name.deleter  # 3.删除行为
    def name(self):
        print("不允许删除")

        
p1 = People('jack')


# 对应查看name行为   
print(p1.name)   # p1.name通过@property访问到的self.__name的属性

# 对应修改name行为
p1.name = 123 # 抛出异常
p1.name = "JACK"  # 正常修改
print(p1.name)

# 对应删除name行为
del p1.name
print(p1.name)

"""
ps:配合property可以先将一个属性藏起来,__name这个属性被藏起来了,藏起来之后定义三个函数都叫name
    这三个函数里面分别写上name的1、2、3的三种行为,跟操作一一对应
"""


# 了解性知识点:这种方式也可以达到上面操作的效果

class People:
    def __init__(self, name):
        self.__name = name

    def get_name(self):
        return self.__name

    def set_name(self, x):
        if type(x) is not str:
            raise Exception("名字必须是字符串类型")
        self.__name = x

    def del_name(self):
        print("不允许删除")

    name = property(get_name, set_name, del_name)

p1 = People('jack')

  

绑定方法与非绑定方法

绑定方法

  • 特点:绑定给谁就应该由谁来调用,谁来调用就会将自己当做第一个参数传入

  • 1.但凡在类中定义一个函数,默认就是绑定给对象的,应该由对象来调用,会将对象当作第一个参数自动传入

    • 注意:绑定给对象的方法,类也能调,但类调就是一个普通函数,需要手动传参,多一个少一个都不行
  • 2.如果想要将函数绑定给类的话就需要用到绑定到类的方法:用classmethod装饰器装饰的方法。类中定义的函数被classmethod装饰过,就绑定给类,应该由类来调用,类来调用会类本身当作第一个参数自动传入

    • 注意:绑定给类的方法,对象也能调,但自动传入的还是类,这样做没有意义,但是要知道对象是可以调的

非绑定方法(静态方法)

  • 特点:不与类和对象绑定,意味着谁都可以来调用,但无论谁来调用就是一个普通函数,没有自动传参的效果

  • 类中定义的函数被staticmethod装饰过,就成一个非绑定的方法即一个普通函数,谁都可以调用,但无论谁来调用就是一个普通函数,没有自动传参的效果

    • 注意:与绑定到对象方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数,对象调用该方法会自动传值,而staticmethod装饰的方法,不管谁来调用,都没有自动传值一说
class People:
    def __init__(self,name):
        self.name = name

    # 但凡在类中定义一个函数,默认就是绑定给对象的,应该由对象来调用,
    # 会将对象当作第一个参数自动传入
    def tell(self):
        print(self.name)

    # 类中定义的函数被classmethod装饰过,就绑定给类,应该由类来调用,
    # 类来调用会类本身当作第一个参数自动传入
    @classmethod
    def f1(cls):  # cls = People
        print(cls)

    # 类中定义的函数被staticmethod装饰过,就成一个非绑定的方法即一个普通函数,谁都可以调用,
    # 但无论谁来调用就是一个普通函数,没有自动传参的效果
    @staticmethod
    def f2(x,y):
        print(x,y)

p1 = People('jack')
p1.tell()

print(People.f1)
People.f1()

print(People.f2)
print(p1.f2)

People.f2(1,2)
p1.f2(3,4)



# 示例场景

'''
# settings.py 配置文件模块

NAME = "xxx"
AGE = 103
GENDER = "male"

'''

import settings

class People:
    def __init__(self,name,age,gender):
      #  self.id = self.create_id()
        self.name = name
        self.age = age
        self.gender = gender

    def tell_info(self):  # 打印详细信息
        print('<%s:%s:%s>' %(self.name,self.age,self.gender))

    @classmethod  # 绑定给类的方法
    def from_conf(cls):  # cls 自动传入类
        print(cls)
        return cls(settings.NAME, settings.AGE, settings.GENDER)

#    @staticmethod  # 非绑定方法 谁都可以来调但没有自动传参效果
#    def create_id():
#        import uuid  # 调用随机产生id模块
#        return uuid.uuid1()  # uuid.uuid1 可以产生随机编号



p1 = People("jack",18,"male")  # 实例化得到一个对象

p2 = People.from_conf()  # 通过配置文件完成实例化 绑定给类的方法应该由类去调
print(p2.__dict__)


# 需求:每个用户都要有一个自己的随机id号
# print(p1.create_id())    
# print(People.create_id())

# print(p1.__dict__)  # 实例化后用户就有自己的id了

  

元类

我们知道,实例对象是由类来创建,那么类又是由什么来创建的呢? 答案就是元类。元类基本都不会用到,但是就算不用到,也应该去熟悉一下概念。

元类:创建类对象的类

类也是对象对象由类创建出来的,类也是对象

元类就是用来创建类的“东西”。你创建类就是为了创建类的实例对象,不是吗?但是我们已经学习到了Python中的类也是对象。

元类就是用来创建这些类(对象)的,元类就是类的类,你可以这样理解为:

num = 18
print(num.__class__)  # <class 'int'>
s = 'abc'
print(s.__class__)  # <class 'str'>

class Person:
    pass

p = Person()  # 使用“类”来创建出实例对象
print(p.__class__)  # <class '__main__.Person'>
"""
通过class方法打印出的结果我们不难发现,它们都是属于同一级别
我们之前学习和使用的都是系统内置的一些类,慢慢我们会发觉系统
给出的这些类满足不了我们的需求,所以我们自己定义出新的类eg:Person
我们可以通过Person类创建出一个具体的实例p

  

你已经看到了type可以让你像这样做:

print(type(num))  # <class 'int'>
print(type(s))  # <class 'str'>
print(type(p))  # <class '__main__.Person'>

  

这是因为函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类。现在你想知道那为什么type会全部采用小写形式而不是Type呢?好吧,我猜这是为了和str保持一致性,str是用来创建字符串对象的类,而int是用来创建整数对象的类。type就是创建类对象的类。你可以通过检查class属性来看到这一点。Python中所有的东西,注意,我是指所有的东西——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类创建而来,这个类就是type。

# 上述我们查看的都是对象对应的类
# 下面我们来证实一下类是不是对象,这个对象又是哪一个类创建出来的
# num 的__class__就是int
print(int.__class__)  # <class 'type'>

# s 的__class__就是str
print(str.__class__)  # <class 'type'>

# p 的__class__就是Person
print(Person.__class__)  # <class 'type'>

# 最后再看看type的__class__依然是type,说明已经到头了
print(type.__class__)  # <class 'type'>
"""
type元类:创建类对象的类,我们得到了证实
概念:
type在往上查找就没了,已经是顶级的存在称之为元类,只能往下去创建其它的类对象,
元类可以实例化出很多很多其它的类(eg:int,str,包括我们自定义的类,等等都是元类实例化出来的类对象)
这些类对象都具备一个特性,具备实例化其它对象的能力
从结构图可以发现是从上往下依次实例化的
"""

  

 

标签:__,.__,name,People,self,绑定,元类,print,property
来源: https://www.cnblogs.com/semwu/p/15658796.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有