• Python之旅第26天(__slots__等内置方法、软件开发规范)


    一、__slots__

      1.__slots__是是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)

      2.使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)

      3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__

       4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该

    class Foo:
        __slots__='x'
    f1=Foo()
    f1.x=1
    f1.y=2#报错
    print(f1.__slots__) #f1不再有__dict__
    
    class Bar:
        __slots__=['x','y']    
    n=Bar()
    n.x,n.y=1,2
    n.z=3#报错
    
    __slots__使用

    二、__doc__(唯一不能被继承的属性)

    class Foo:
        '我是描述信息,不跟基类走'
        pass
    class Bar(Foo):
        pass
    print(Bar.__doc__) #该属性无法继承给子类
    
    该属性无法被继承

    三、__module__和__class__

      __module__ 表示当前操作的对象在那个模块

      __class__     表示当前操作的对象的类是什么

    from lib.aa import C
    
    obj = C()
    print obj.__module__  # 输出 lib.aa,即:输出模块
    print obj.__class__      # 输出 lib.aa.C,即:输出类

    四、__del__构析方法(非常牛逼,但对我来说没卵用的方法)

      发生内存变动时会触发这个方法,比如删除对象,增加对象,关闭文件,关闭运行,等等

      但是我又不操作,是Python解释器用的

    class Foo:
        def __del__(self):
            print('执行我啦')
    f1=Foo()
    del f1
    print('------->')
    #输出结果
    # 执行我啦
    # ------->
    
    class Foo:
    
        def __del__(self):
            print('执行我啦')
    
    f1=Foo()
    # del f1
    print('------->')
    # 文件执行完毕,依旧会清理内存,所以还是会触发__del__方法
    # 输出结果
    # ------->
    # 执行我啦

    五、__call__

    对象后面加括号,触发执行。

    注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

    class Foo:
        def __init__(self):
    pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 执行 __init__ obj() # 执行 __call__

    六、__iter__和__next__

      这两个东东就是之前实现迭代器的东西,其实每个类都是有这两个方法的,我们可以通过重新设置他们来实现一些功能,比如实现一个斐波那契数列

    class Fele:
        def __init__(self):
            self.a = 0
            self.b = 1
    
        def __iter__(self):
            return self
    
        def __next__(self):
            self.a , self.b = self.b , self.a + self.b
            return self.a
        # 这样就实现了输出斐波那契数列
    f1 = Fele()
    for i in f1:
        if i < 10000:
            print(i)

    七、描述符(__get__,__set__,__delete__)

      描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议

      __get__():调用一个属性时,触发

      __set__():为一个属性赋值时,触发

      __delete__():采用del删除属性时,触发

    class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
        def __get__(self, instance, owner):
            pass
        def __set__(self, instance, value):
            pass
        def __delete__(self, instance):
            pass
    
    定义一个描述符

      描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)

    class Foo:
        def __get__(self, instance, owner):
            print('触发get')
        def __set__(self, instance, value):
            print('触发set')
        def __delete__(self, instance):
            print('触发delete')
    
    #包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法
    f1=Foo()
    f1.name='egon'
    f1.name
    del f1.name
    #疑问:何时,何地,会触发这三个方法的执行
    
    引子:描述符类产生的实例进行属性操作并不会触发三个方法的执行

      

    #描述符Str
    class Str:
        def __get__(self, instance, owner):
            print('Str调用')
        def __set__(self, instance, value):
            print('Str设置...')
        def __delete__(self, instance):
            print('Str删除...')
    
    #描述符Int
    class Int:
        def __get__(self, instance, owner):
            print('Int调用')
        def __set__(self, instance, value):
            print('Int设置...')
        def __delete__(self, instance):
            print('Int删除...')
    
    class People:
        name=Str()
        age=Int()
        def __init__(self,name,age): #name被Str类代理,age被Int类代理,
            self.name=name
            self.age=age
    
    #何地?:定义成另外一个类的类属性
    
    #何时?:且看下列演示
    
    p1=People('alex',18)
    
    #描述符Str的使用
    p1.name
    p1.name='egon'
    del p1.name
    
    #描述符Int的使用
    p1.age
    p1.age=18
    del p1.age
    
    #我们来瞅瞅到底发生了什么
    print(p1.__dict__)
    print(People.__dict__)
    
    #补充
    print(type(p1) == People) #type(obj)其实是查看obj是由哪个类实例化来的
    print(type(p1).__dict__ == People.__dict__)
    
    描述符应用之何时?何地?

    4 注意事项:
      一 描述符本身应该定义成新式类,被代理的类也应该是新式类

         二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中

      三 要严格遵循该优先级,优先级由高到底分别是
      1.类属性

      2.数据描述符

        3.实例属性

        4.非数据描述符

      5.找不到的属性触发__getattr__()

    #描述符Str
    class Str:
        def __get__(self, instance, owner):
            print('Str调用')
        def __set__(self, instance, value):
            print('Str设置...')
        def __delete__(self, instance):
            print('Str删除...')
    
    class People:
        name=Str()
        def __init__(self,name,age): #name被Str类代理,age被Int类代理,
            self.name=name
            self.age=age
    
    
    #基于上面的演示,我们已经知道,在一个类中定义描述符它就是一个类属性,存在于类的属性字典中,而不是实例的属性字典
    
    #那既然描述符被定义成了一个类属性,直接通过类名也一定可以调用吧,没错
    People.name #恩,调用类属性name,本质就是在调用描述符Str,触发了__get__()
    
    People.name='egon' #那赋值呢,我去,并没有触发__set__()
    del People.name #赶紧试试del,我去,也没有触发__delete__()
    #结论:描述符对类没有作用-------->傻逼到家的结论
    
    '''
    原因:描述符在使用时被定义成另外一个类的类属性,因而类属性比二次加工的描述符伪装而来的类属性有更高的优先级
    People.name #恩,调用类属性name,找不到就去找描述符伪装的类属性name,触发了__get__()
    
    People.name='egon' #那赋值呢,直接赋值了一个类属性,它拥有更高的优先级,相当于覆盖了描述符,肯定不会触发描述符的__set__()
    del People.name #同上
    '''
    
    类属性>数据描述符
    #描述符Str
    class Str:
        def __get__(self, instance, owner):
            print('Str调用')
        def __set__(self, instance, value):
            print('Str设置...')
        def __delete__(self, instance):
            print('Str删除...')
    
    class People:
        name=Str()
        def __init__(self,name,age): #name被Str类代理,age被Int类代理,
            self.name=name
            self.age=age
    
    
    p1=People('egon',18)
    
    #如果描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调用与赋值都是触发描述符的操作,于p1本身无关了,相当于覆盖了实例的属性
    p1.name='egonnnnnn'
    p1.name
    print(p1.__dict__)#实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了
    del p1.name
    
    数据描述符>实例属性
    class Foo:
        def func(self):
            print('我胡汉三又回来了')
    f1=Foo()
    f1.func() #调用类的方法,也可以说是调用非数据描述符
    #函数是一个非数据描述符对象(一切皆对象么)
    print(dir(Foo.func))
    print(hasattr(Foo.func,'__set__'))
    print(hasattr(Foo.func,'__get__'))
    print(hasattr(Foo.func,'__delete__'))
    #有人可能会问,描述符不都是类么,函数怎么算也应该是一个对象啊,怎么就是描述符了
    #笨蛋哥,描述符是类没问题,描述符在应用的时候不都是实例化成一个类属性么
    #函数就是一个由非描述符类实例化得到的对象
    #没错,字符串也一样
    
    
    f1.func='这是实例属性啊'
    print(f1.func)
    
    del f1.func #删掉了非数据
    f1.func()
    
    实例属性>非数据描述符
    class Foo:
        def __set__(self, instance, value):
            print('set')
        def __get__(self, instance, owner):
            print('get')
    class Room:
        name=Foo()
        def __init__(self,name,width,length):
            self.name=name
            self.width=width
            self.length=length
    
    
    #name是一个数据描述符,因为name=Foo()而Foo实现了get和set方法,因而比实例属性有更高的优先级
    #对实例的属性操作,触发的都是描述符的
    r1=Room('厕所',1,1)
    r1.name
    r1.name='厨房'
    
    
    
    class Foo:
        def __get__(self, instance, owner):
            print('get')
    class Room:
        name=Foo()
        def __init__(self,name,width,length):
            self.name=name
            self.width=width
            self.length=length
    
    
    #name是一个非数据描述符,因为name=Foo()而Foo没有实现set方法,因而比实例属性有更低的优先级
    #对实例的属性操作,触发的都是实例自己的
    r1=Room('厕所',1,1)
    r1.name
    r1.name='厨房'
    
    再次验证:实例属性>非数据描述符
    class Foo:
        def func(self):
            print('我胡汉三又回来了')
    
        def __getattr__(self, item):
            print('找不到了当然是来找我啦',item)
    f1=Foo()
    
    f1.xxxxxxxxxxx
    
    非数据描述符>找不到

    描述符这里引用的是教学视频中的演示案例,能记住,但是还没理解

    描述符这个鬼地方我还不是很明白,明天得抓紧看看是怎么一回事

    八、软件开发规范

    虽然上面还有我没太搞明白的东西,但是,我认为今晚上和第八点比起来,上面讲到的都是垃圾

    暂时放张图片,后面还是得自己操作一下才能深刻体会

    上面这个图是在学完类之后的作业,就是这么个规范

    文件目录说的麻烦点,就是下面这个顺序

    Foo/
    |-- bin/
    |   |-- foo
    |
    |-- foo/
    |   |-- tests/
    |   |   |-- __init__.py
    |   |   |-- test_main.py
    |   |
    |   |-- __init__.py
    |   |-- main.py
    |
    |-- docs/
    |   |-- conf.py
    |   |-- abc.rst
    |
    |-- setup.py
    |-- requirements.txt
    |-- README


    简要解释一下:

    1. bin/: 存放项目的一些可执行文件,当然你可以起名script/之类的也行。
    2. foo/: 存放项目的所有源代码。(1) 源代码中的所有模块、包都应该放在此目录。不要置于顶层目录。(2) 其子目录tests/存放单元测试代码; (3) 程序的入口最好命名为main.py
    3. docs/: 存放一些文档。
    4. setup.py: 安装、部署、打包的脚本。
    5. requirements.txt: 存放软件依赖的外部Python包列表。
    6. README: 项目说明文件。

    今晚就是这些喽,最近工作量大了,估计学习内容又得缩减了,不然学的不深刻,而且准确的说,我前面的已经开始忘记了,该复习了!!!

      

  • 相关阅读:
    ReactNative问题随记1 Exception in thread "main" java.lang.RuntimeException: gradle-2.14.1-all.zip
    Win10 安装 VMWare中 MAC OS X的安装,VMWare tools的配置与iOS的Helloworld
    JavaScript 异步编程的前世今生(上)
    老司机带路:《axios从入门到开车 嘀嘀~~》
    如何在微信小程序定义全局变量、全局函数、如何实现 函数复用 模块化开发等问题详解
    详解AMD规范及具体实现requireJS在工程中的使用
    如何让浏览器支持ES6语法,步骤详细到小学生都能看懂!
    对JS闭包和函数作用域的问题的深入讨论,如何理解JS闭包和函数作用域链?
    混合物App开发中,在移动设备上调试查看日志,重写window.console
    HTML5调用手机摄像机、相册功能 <input>方法
  • 原文地址:https://www.cnblogs.com/xiaoyaotx/p/12528901.html
Copyright © 2020-2023  润新知