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: 项目说明文件。

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

  

原文地址:https://www.cnblogs.com/xiaoyaotx/p/12528901.html