• 面向对象03 封装 反射 内置方法


    封装

    1. 广义:把属性或者方法装起来,外面就不能调用了,要通过类的名字去调用

    2. 狭义:把属性或者方法藏起来,外面就不能调用了,只能在内部偷偷使用

      给一个名字前面加上了双下划线的时候,这个名字就变成了一个私有的

      class User:
          __country = 'China'
          def __init__(self,name,passwd):
              self.usr = name
              self.__pwd = passwd  	# 私有的实例变量/私有的对象属性
          def func(self):
              print(__country)
      alex = User('alex','sbsbsb')
      print(alex.__pwd)  	# 报错
      print(alex.pwd)    	# 报错
      print(User.__country)	# 报错
      alex.func()		# China
      '''
      解析:设置私有的实例变量或私有的对象属性后,无法通过正常的查看属性方法的方式对其查看。
      '''
      
    3. 查看私有实例变量或私有的属性

      获取私有实例变量的方法:我们可以在类的内部定义一个方法,把这个私有实例变量作为函数的返回值,这样我们可以通过调用这个方法来获取返回值,从而获取这个私有实例变量。

      获取私有方法的方法:我们可以在类的内部定义一个方法,在这个方法中执行这个私有方法,self.__名称(),作为返回值直接返回。

      import  hashlib
      class User:
          def __init__(self,name,passwd):
              self.usr = name
              self.__pwd = passwd  	# 私`有的实例变量
          def __get_md5(self):     	# 私有的绑定方法
              md5 = hashlib.md5(self.usr.encode('utf-8'))
              md5.update(self.__pwd.encode('utf-8'))
              return md5.hexdigest()
          def getpwd(self):
              return self.__get_md5()
      alex = User('alex','sbsbsb')
      print(alex.getpwd())
      '''
      解析:我们在私有方法__get_md5中针对用户名密码加密,当想在类的外部查看时,可以通过对象名.getpwd()查看加密后的结果.
      
      '''
      
    4. 私有的原理:

      在我们在定义静态变量或方法前面加上__时,在内存中存储的是当前这句话所在的类的名字拼在私有变量,所以我们在外部查不到,而且在外部不能定义私有的概念。而我们在类内部使用的时候自动的把当前这句话所在的类的名字拼在私有变量前完成变形,所以可以进行操作。

      class Foo(object):
          def __init__(self):
              self.__func()
          def __func(self):
              print('in Foo')
      class Son(Foo):
          def __func(self):
              print('in Son')
      Son()
      '''
      in son
      当前这句话所在的类的名字拼在私有变量前完成变形,即__init__所在的类Foo,所以执行的是Foo中的__func
      '''
      

      注意:私有的内容不能被子类使用!

      class Foo(object):
          def __func(self):
              print('in Foo')
      class Son(Foo):
          def __init__(self):
              self.__func()
      Son()
      '''
      报错
      因为在Son空间中没有__func。(只在本类的空间)
      '''
      
    5. 在其他语言中的数据的级别都有哪些?在python中有哪些?
      public  公有的 类内类外都能用,父类子类都能用         python支持
      protect 保护的 类内能用,父类子类都能用,类外不能用    python不支持
      private 私有的 本类的类内部能用,其他地方都不能用     python支持
      
      • 使用私有的三种情况

        让你看让你改

        可看不可改 (类 的内部实现)

        可看可改 (类 的内部实现)

      • 封装的语法

        私有的静态变量

        私有的实例变量

        私有的绑定方法

      • 私有的特点

        可以在类的内部使用

        不能在类的外部使用

        不能在类的子类中使用

      • 原理

        变形:当前这句话所在的类的名字拼在私有变量前完成变形

        在类的内部定义的时候变形

      • 类中的变量的级别

        共有的 python支持

        保护的 python不支持

        私有的 python支持

    6. 类中的三个装饰器(内置函数)

      1. property

        1. 把一个方法(不能有参数)伪装成一个属性,例:
            from math import pi
            class Circle:
                def __init__(self,r): self.r = r
                @property   # 把一个方法伪装成一个属性,在调用这个方法的时候不需要加()就可以直接得到返回值
                def area(self): return pi * self.r**2
            c1 = Circle(5)
            print(c1.r)
            print(c1.area)
        
            '''
            解析:@property把一个方法伪装成一个属性,在调用这个方法的时候不需要加()就可以直接得到返回值
            如上式中我们想查看c1的面积只需要c1.area就可,省略了()
        	'''
        
        1. 第二个应用场景:和私有的属性合作

          class Goods:
              discount = 0.8
              def __init__(self,name,origin_price):
                  self.name = name
                  self.__price = origin_price
              @property
              def price(self):
                  return self.__price * self.discount
          apple = Goods('apple',5)
          print(apple.price)		# 4.0
          
        2. 进阶 —— 对私有属性的更改

          class Goods:
              discount = 0.8
              def __init__(self,name,origin_price):
                  self.name = name
                  self.__price = origin_price
              @property
              def price(self):
                  return self.__price * self.discount
              
              @price.setter			# 装饰器
              def price(self,new_value):
                  if isinstance(new_value,int):
                      self.__price = new_value
                      
              @price.deleter
              def price(self):
                  del self.__price
                  
          apple = Goods('apple',5)
          print(apple.price)   # 调用的是被@property装饰的price
          apple.price = 10     # 调用的是被setter装饰的price
          print(apple.price)
          
          del apple.price		# 调用的是被deleter装饰的prcie (并不能真正删除什么,而是调用此方法,此方法内部有删除price的代码而已。
          
          '''
          4.0
          8.0
          '''
          
      2. classmethod —— 被装饰的方法会成为类方法

        1. 作用:把一个对象的绑定方法,修改成一个 类方法

        2. 优点:

          • 在方法中仍然可以引用类中的静态变量
          • 可以不用实例化对象,就直接用类名在外部调用这个方法
          • 可以通过类名调用方法,也可以通过对象名调用方法
        3. 何时使用:

          • 定义了一个方法,默认穿self,但这个self没被使用
          • 并且你在这个方法里用到了当前的类名,或者你准备使用这个类的内存空间中的名字的时候
          class Goods:
              __discount = 0.8
              def __init__(self):
                  self.__price = 5
                  self.price = self.__price * self.__discount
              @classmethod   # 把一个对象绑定的方法 修改成一个 类方法
              def change_discount(cls,new_discount):
                  cls.__discount = new_discount
           
          Goods.change_discount(0.6)   # 类方法可以通过类名调用
          apple = Goods()
          print(apple.price)
          apple.change_discount(0.5)  # 类方法可以通过对象名调用
          apple2 = Goods()
          print(apple2.price)
          
          '''
          3.0
          2.5
          '''
          
        4. 例子 一定理解!!

          import time
          class A:
              def __init__(self,year,month,day):
                  self.year = year
                  self.month = month
                  self.day = day
              @classmethod
              def today(cls):
                  year = time.localtime().tm_year
                  month = time.localtime().tm_mon
                  day = time.localtime().tm_mday
                  return cls(year,month,day)
          t_day = A.today()
          print(t_day.year,t_day.month,t_day.day)
          # 2019 6 5
          
      3. staticmethod —— 被装饰的方法会成为静态方法

        1. 作用:把一个普通函数挪到类中来直接使用,制造静态方法用的

        2. 何时使用:本身是一个普通的函数,要移动到类的内部执行,那么直接给这个函数添加@staticmethod装饰器就可以了。

        3. 在函数的内部既不会用到self变量,也不会用到cls类

          class User:
              pass
              @staticmethod
              def login(a,b):      # 本身是一个普通的函数,被挪到类的内部执行,那么直接给这个函数添加@staticmethod装饰器就可以了
                  print('登录的逻辑',a,b)
                  # 在函数的内部既不会用到self变量,也不会用到cls类
          obj = User()
          User.login(1,2)
          obj.login(3,4)
          '''
          登录的逻辑 1 2
          登录的逻辑 3 4
          '''
          
        4. 能定义到类中的内容:

        class A:
            country = '中国'
            def func(self):
                print(self.__dict__)
            @classmethod
            def clas_func(cls):
                print(cls)
            @staticmethod
            def stat_func():
                print('普通函数')
            @property
            def name(self):
                return 'wahaha'
        
        • 静态变量 — 是个所有对象共享的变量 由对象/类调用。

        • 绑定方法 — 是个自带self参数的函数 由对象调用

        • 类方法 — 是个自带cls参数的函数 由对象/类调用

        • 静态方法 — 是个什么都不带的普通函数 由对象/类调用

        • property属性:是个伪装成属性的方法 由对象调用,但不加括号

    7. 反射 ( getattr() hasattr())
      1. 定义:用字符串数据类型的名字,来操作这个名字对应的函数实例变量绑定方法等

      2. 何时使用:在知道一个变量的字符串数据类型的名字,你想直接调用他,但是你调不到

        应用场景:

        • 反射 对象的实例变量
        • 反射类的 静态变量/绑定方法/其他方法
        • 模块中的 所有的变量
          • 被导入的模块 模块
          • 当前执行的py文件 - 脚本
      3. 举例:

        1. 反射本脚本下类中的方法或实例变量等
            class Person:
                def __init__(self,name,age):
                    self.name = name
                    self.age = age
                def qqxing(self):
                    print('in qqxing')
        
            alex = Person('alex',83)
            wusir = Person('wusir',74)
            ret = getattr(alex,'name')
            print(ret)						# alex
            ret = getattr(wusir,'qqxing')	# in qqxing
            ret()
        
            '''
            解析:我们创建一个类alex,当我查看alex中的变量及方法须使用alex.想查看的内容
            但如果查看的内容如果是一个待输入的即字符串类型,我们无法执行。
            此时,我们可以使用getattr(alex,想查看的内容) 《=》alex.想查看的内容(去除字符串格式后)
        
            '''
        
        1. 反射导入的模块中的类

          import a 
          print(getattr(a, 'Alipay'))
          print(getattr(a, 'Wechat'))
          # 导入模块a中的类的内存地址,可以在其后加()按照格式创建对象
          
        2. 反射当前脚本下的类,函数名,变量等

          import sys
          s1 = '加油老哥们!'
          class Person:
              def __init__(self,name,age):
                  self.name = name
                  self.age = age
                  
          print(getattr(sys.modules['__main__'],'s1'))
          ret = getattr(sys.modules['__main__'],'Person')
          nie = ret('alex',18)
          print(nie.__dict__)
          
      4. hasattr()

        • 当我们使用getattr()时,如果找不到就会报错,所以在使用之前应该先判断我们要寻找的对应的方法或者变量是否存在。

        • callable 当我们想对我们输入的内容进行操作,但不知道其对应的是方法,函数还是变量,所以此时我们需判断其是否可调用,可调用就加()在后,否则不加

          class A:
              Role = '治疗'
              def __init__(self):
                  self.name = 'alex'
                  self.age = 84
              def func(self):
                  print('wahaha')
          
          a = A()
          print(hasattr(a,'sex'))		# False
          print(hasattr(a,'age'))		# True
          print(hasattr(a,'func'))	# True
          if hasattr(a,'func'): ————》此时为True
              if callable(getattr(a,'func')):	 ————》func为方法,可调用
                  getattr(a,'func')()	————》 所以调用func
          
          1. 一些内置的魔术方法
      5. __new__ !!!!! (构造方法)
        1. 作用:创建一个对象需要的空间

        2. 在实例化的时候先创建一块对象空间,有一个类指针指向类 ——> __new__ ,然后再调用__init__.

          class A:
              def __new__(cls, *args, **kwargs):   # 构造方法
                  # o = super().__new__(cls)
                  o = object.__new__(cls)		# 寻找父类中__new__方法并创建一个class A的对象
                  print('执行new',o)
                  return o					# 返回在父类中得到的对象
              def __init__(self):		# __init__接收到__new__返回的对象给予self
                  print('执行init',self) 
          A()
          
          '''
          解析:当我们实例化A时,总是先执行__new__方法,而方法中的内容为寻找并执行父类(object类)的__new__方法,把返回的对象赋予给__Init__的self并执行__init__的函数。
          '''
          
        3. 设计模式 —— 单例模式(一个类 从头到尾 只会创建一次self的空间)

          class A:
              __statu = None
              def __new__(cls, *args, **kwargs):
                  if not cls.__statu:
                      cls.__statu = super().__new__(cls)
                  return cls.__statu
              def __init__(self,name,age):
                  self.name = name
                  self.age = age
          person = A('NIE',18)
          print(person.name,person.age)	# NIE 18
          person2 = A('sui',18)
          print(person.name,person.age)	# sui 18
          print(person2.name,person2.age)	# sui 18
          
          '''
          解析:类中设置了一个私有的静态变量 __statu = None,在进行实例化时先执行A类中的__new__方法,判断__statu是否为空如果为空,则寻找并执行父类的__new__方法,开辟了一个空间,创建了一个A类的对象,并重新赋值给 __statu,当再次实例化时__statu已被赋值,所以不会重新开辟空间创建对象。所以所有A类的对象都使用同一个空间。
          
          '''
          

          python中的 import 导入的都是单例模式

      6. __call__

        当判断对象()是否可执行时,使用callable(对象),也就是执行对象内部的__call__方法。

      7. __len__

        获取对象长度。当使用len(对象)时也就是执行对象内部的__len__的方法

        class Cls:
            def __init__(self,name):
                self.name = name
                self.students = []
            def len(self):				
                return len(self.students)
            def __len__(self):
                return len(self.students)
        py22 = Cls('py22')
        py22.students.append('杜相玺')
        py22.students.append('庄博')
        py22.students.append('大壮')
        print(py22.len())		# 3  调用类的绑定方法
        print(len(py22))		# 3  调用内置函数
        
      8. __str____repr__

        1. 作用:两者的目的都是为了显式的显示对象的一些必要信息,方便查看和调试。

        2. 使用场景:当我想打印对象就想得到对象的某些信息时。此时直接打印无法完成。

        3. 注意:

          • 只有在print()打印一个对象 用%s进行字符串拼接 或者str(对象)总是调用这个对象的__str__方法
          • __str__只能返回字符串,所以当返回多个信息时需要进行拼接。

          如下例的:','.join([self.name,str(self.price),self.period])

          class Course:
              def __init__(self,name,price,period):
                  self.name = name
                  self.price = price
                  self.period = period
              def __str__(self):
                  return ','.join([self.name,str(self.price),self.period])	# 字符串拼接
          python = Course('python',21800,'6 months')
          linux = Course('linux',19800,'5 months')
          mysql = Course('mysql',12800,'3 months')
          go = Course('go',15800,'4 months')
          print(go)			# go,15800,4 months	
          print(python)		# python,21800,6 months
          print(mysql)		# mysql,12800,3 months
          lst = [python, linux, mysql, go]
          for index,c in enumerate(lst,1):
              print(index,c)
          num = int(input('>>>'))
          course = lst[num-1]
          print('恭喜您选择的课程为 %s  价格%s元'%(course.name,course.price))
          
          
        4. __repr__

          __repr__不仅是__str__的替代品,还有自己的功能
          用%r进行字符串拼接 或者用repr(对象)的时候总是调用这个对象的__repr__方法
          
          class clas:
              def __init__(self):
                  self.student = []
              def append(self,name):
                  self.student.append(name)
              def __repr__(self):
                  return str(self.student)
              def __str__(self):
                  return 'aaa'
          py22 = clas()
          py22.append('大壮')
          print(py22)				# aaa 执行__str__
          print(str(py22))		# aaa 执行__str__
          print('我们py22班 %s'%py22)	# 我们py22班 aaa	执行__str__
          print('我们py22班 %r'%py22)	# 我们py22班 ['大壮']
          print(repr(py22))		# ['大壮']
          
  • 相关阅读:
    day02操作系统/编程语言分类/python解释器介绍/python解释器多版本共存
    网络编程-互联网协议(网络编程)/基于TCP协议的套接字通信/加上通信循环/加上连接循环
    每天新的英语单词
    re模块(正则表达式)
    包的使用/time与datetime模块/random模块/打印进度条/shutil模块
    模块的使用之import/模块的使用之 from import/区分python文件的两种用途
    列表生成式与生成器表达式模块的使用模块的使用之from.......import区分python文件的两种用途文件的搜索路径
    三元表达式/函数的递归/匿名函数及其应用/map函数/reduce函数/ filter函数
    函数的嵌套/名称空间/作用域/函数对象/闭包函数
    跟未名学Office
  • 原文地址:https://www.cnblogs.com/lianzibing/p/10993705.html
Copyright © 2020-2023  润新知