首先来看下对象的表现形式:
class People():
    def __init__(self,name,age):
        self.name=name
        self.age=age if __name__== "__main__":
    p=People('zhf','30')
    print p
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter9.py
<__main__.People instance at 0x01763B48>
当我们打印People的实例的时候得到的是这个类的内存地址。这个对用户而言其实没太多的用处。我们想在打印的时候得到关于这个类的更多的信息。这个时候就要用到__str__.代码修改如下:
class People():
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __str__(self):
        return 'People:%s,%s' % (self.name,self.age) if __name__== "__main__":
    p=People('zhf','30')
    print p
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter9.py
People:zhf,30
得到的结果是People:zhf,30。这样就更直接也更直观。__str__的作用在于print对象实例的时候会被调用。但是我们如果不用print p而是直接用p的时候,__str__则不会被调用。这个时候就要用到__repr__。代码改成如下的时候。在单独调用People实例的时候也可以打印出相应的信息。
class People():
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __str__(self):
        return 'People:%s,%s' % (self.name,self.age)
    def __repr__(self):
        return 'People:%s,%s' % (self.name,self.age)
其实__str__和__repr__的返回都是一样的,每一个都写上一个实现其实有点冗余。代码可以精简如下,这样不管是print打印还是单独调用都可以打印。
class People():
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __str__(self):
        return 'People:%s,%s' % (self.name,self.age)
    __repr__=__str__
通过两种不同的调用方式可以看出__str__主要是显示给用户的。而__repr__主要是给开发人员使用的。
我们再来看下更多的内置方法:
class Vector():
    def __init__(self,x,y):
        self.x=float(x)
        self.y=float(y)
    def __iter__(self):
        return (i for i in (self.x,self.y))
    def __str__(self):
        return str(tuple(self))
    def __bytes__(self):
        return (bytes([ord(self.typecode)])+bytes(array(self.typecode,self)))
    def __eq__(self, other):
        return tuple(self) == tuple(other)
    def __abs__(self):
        return math.hypot(self.x,self.y)
    def __bool__(self):
        return bool(abs(self))
if __name__== "__main__":
    v1=Vector(3,4)
    v2=Vector(3,1)
    x,y=v1          #调用__iter__
    print x,y
    print abs(v1)   #调用__abs__
    print v1==v2    #调用__eq__
    print bool(v1)  #调用__bool__
    print bytes(v1) #调用__bytes__
从上面的实现可以看到,当对对象实例采用各自不同的运算的时候,会调用相应的内部方法。
 
classmethod与staticmethod
我们先来看下一个普通的类实例调用方法:
class People():
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __str__(self):
        return 'People %s,%s' % (self.name,self.age)
    def print_info(self):
        print self.name,self.age
    __repr__=__str__
 
if __name__== "__main__":
    p1=People('zhf',30)
    p2=People('zhf1',32)
    p1.print_info()
    p2.print_info()
在这里有两个实例化的对象,p1和p2.这里的实例对象是通过self参数传递给函数的。来看下运行状态
P1的实例对象,可以看到self参数是被不同的实例对象给赋值的

P2的实例对象

因此在打印的时候,也是将不同的实例对象传入至函数,这样就能打印出不同的实例对象的参数。

在__init__中定义的参数都属于各自的实例对象。如果我们仅想实现类的交互而不是实例的交互呢,比如我想统计这个对象有多少个实例。下面的代码是否可以呢?
class cls_instance(object):
    inst_num=0
    def __init__(self):
        self.inst_num+=1 if __name__== "__main__":
    c1=cls_instance()
    c2=cls_instance()
    print c2.inst_num
通过执行结果可以看到实例个数为1.但其实有C1和C2两个实例化的对象。从下面的关系图可以看到,self.inst_num是被各自实例计数保存的。并没有做到全局共享,要做到全局共享,就必须以类的方式交互而不是实例的方式

这里就要用到classmethod这个装饰器了。代码修改如下:
class cls_instance(object):
    inst_num=0
    def __init__(self):
        cls_instance.inst_num+=1
    @classmethod
    def get_no_instance(cls):
        return cls.inst_num if __name__== "__main__":
    c1=cls_instance()
    c2=cls_instance()
    print c2.get_no_instance()
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter9.py
2
首先在__init__中调用的是cls_instance.inst_num+=1。也就是通过类来对计数器进行加一,而非实例对象。然后通过get_no_instance来统计实例对象。注意,get_no_instance的参数为cls且前面带有装饰器@classmethod,因此这个cls就是代表的cls_instance这个类对象,而非实例。所以得到的实例个数就为2个

从上面的关系图很容易看出,get_no_instance中的cls参数为cls_instance类。这就是classmethod的用法。我们来看一个classmethod更实用的用法。
如果我们想实例化一个时间的类,传入具体的年,月,日
class Date_format(object):
    def __init__(self,day=0,month=0,year=0):
        self.day=day
        self.month=month
        self.year=year
if __name__== "__main__":
    date='2017-7-10'
   
day,month,year=map(int,date.split('-'))
    d=Date_format(day,month,year)
时间的格式是2017-7-10,为了区分年,月,日,首先要按照这个格式分割开来并且转换为整数。
但是如果我们有很多类似这种格式的格式字符串信息。这样一个个的去实例也挺麻烦的。我们需要把day,month,year=map(int,date.split('-'))这个的具体实现做到类中去。通过调用类来得到一个时间实例。
class Date_format(object):
    def __init__(self,day=0,month=0,year=0):
        self.day=day
        self.month=month
        self.year=year
    @classmethod
    def from_string(cls,date_string):
        day,month,year=map(int,date_string.split('-'))
        date=cls(day,month,year)
        return date if __name__== "__main__":
    d=Date_format.from_string('2017-7-10')
    print d.day,d.month,d.year
通过from_string来得到具体的日期并通过date=cls(day,month,year)来实例化对象并返回这个实例化对象
 
下面来看下staticmethod。还是刚才的例子,如果我们只是想得到具体的日期,而不需要实例化的话可以用到staticmethod。如下的代码:
class Date_format(object):
    def __init__(self,day=0,month=0,year=0):
        self.day=day
        self.month=month
        self.year=year
    @classmethod
    def from_string(cls,date_string):
        day,month,year=map(int,date_string.split('-'))
        date=cls(day,month,year)
        return date
    @staticmethod
    def get_detail_time(date_string):
        day,month,year=map(int,date_string.split('-'))
        return day,month,year if __name__== "__main__":
    day,month,year=Date_format.get_detail_time('2017-7-10')
    print day,month,year
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter9.py
2017 7 10
__slots__
 
class A(object):
    def __init__(self,x):
        self.x=x if __name__== "__main__":
    a=A(3)
    print a.x
    print a.__dict__
    print a.__dict__['x']
a.y=1
print a.__dict__
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter9.py
3
{'x': 3}
3
{'y': 1, 'x': 3}
在python中,实例化对象的变量都保存在字典中,因此我们可以用a.__dict__来查看所有的属性。也可以通过a.__dict__[‘x’]来代替a.x。本质上原理都是一样的。另外也可以自由添加变量。比如a.y=1.添加成功后再字典中也能看到对应的变量。但是如果我们想固定变量,比如只有x和y该如何操作呢。这里就需要用到__slots__方法。代码修改如下:
class A(object):
    __slots__=('x','y')
    def __init__(self,x,y):
        self.x=x
if __name__== "__main__":
    a=A(3,2)
    print a.x
    print a.__slots__
print a.__dict__
我们来看下运行结果:
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter9.py
3
('x', 'y')
Traceback (most recent call last):
  File "E:/py_prj/fluent_python/chapter9.py", line 65, in <module>
    print a.__dict__
AttributeError: 'A' object has no attribute '__dict__'
首先__slots__是返回的元组形式存储变量,而非字典。另外在执行__dict__的时候报错,提示没有__dict__属性。因此如果用了__slots__来设定变量,则__dict__将不会再有。那么我们可以添加变量么。我们新增一个变量z
a.z=3
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter9.py
Traceback (most recent call last):
  File "E:/py_prj/fluent_python/chapter9.py", line 65, in <module>
    a.z=3
AttributeError: 'A' object has no attribute 'z'
结果提示没有z的属性。说明使用了__slots__后不能任意添加属性。
那么__slots__的优势在哪呢?不用__slots__的时候,变量是保存在字典里的。字典是很耗内存的,如果这个对象有很多个实例对象,那么每个实例对象都将会建立一个字典。在这种情况下内存的消耗是很惊人的。我们来实测一下。我们用profile来测试代码所占的内存。首先创建100000个实例对象
class A(object):
    def __init__(self,x,y):
        self.x=x
        self.y=y
@profile
def profile_result():
    f=[A(1,2) for i in range(100000)] if __name__== "__main__":
    profile_result()
测试结果:建立实例对象消耗了21.4M的内存
改用__slots__的方式:
class A(object):
    __slots__=('x','y')
    def __init__(self,x,y):
        self.x=x
        self.y=y
@profile
def profile_result():
    f=[A(1,2) for i in range(100000)]
if __name__== "__main__":
    profile_result()
测试结果:同样个数的实例值消耗了5.7M的内存。相比字典确实节省了不少的内存
最后总结一下:
__slots__用元组的形式保存变量,而非字典。且__slots__无法添加变量。由于采用了元组的方式,因此在创建多个实例的时候,会节省不少内存。但由于无法添加变量,也有很大的局限性。最适合__slots__的场景就是类的变量恒定不变且会创建多个实例化对象的时候,可以采用__slots__
 
 
 


												

流畅的python学习笔记:第九章:符合python风格的对象的更多相关文章

  1. o'Reill的SVG精髓(第二版)学习笔记——第九章

    第九章:文本 9.1 字符:在XML文档中,字符是指带有一个数字值的一个或多个字节,数字只与Unicode标准对应. 符号:符号(glyph)是指字符的视觉呈现.每个字符都可以用很多不同的符号来呈现. ...

  2. 《机器学习实战》学习笔记第九章 —— 决策树之CART算法

    相关博文: <机器学习实战>学习笔记第三章 —— 决策树 主要内容: 一.CART算法简介 二.分类树 三.回归树 四.构建回归树 五.回归树的剪枝 六.模型树 七.树回归与标准回归的比较 ...

  3. Python学习笔记 -- 第一章

    本笔记参考廖雪峰的Python教程 简介 Python是一种计算机高级程序设计语言. 用Python可以做什么? 可以做日常任务,比如自动备份你的MP3:可以做网站,很多著名的网站包括YouTube就 ...

  4. python学习笔记:安装boost python库以及使用boost.python库封装

    学习是一个累积的过程.在这个过程中,我们不仅要学习新的知识,还需要将以前学到的知识进行回顾总结. 前面讲述了Python使用ctypes直接调用动态库和使用Python的C语言API封装C函数, C+ ...

  5. Python学习笔记:第一天python基础

    目录 1. python简介 2. python的安装 3. 编写第一个helloword 4. 变量和常量 5. 数据类型 6. 输入 7. if语句 1. python简介 python是在198 ...

  6. Python学习笔记(四)Python程序的控制结构

    在学习了 Python 的基本数据类型后,我们就要开始接触Python程序的控制结构,了解 Python 是如何使用控制结构来更改程序的执行顺序以满足多样的功能需求.如果有的小伙伴在之前学过C语言,j ...

  7. Python学习笔记-Linux下安装Python

    Linux系统CentOS 1.安装依赖组件 yum install openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel s ...

  8. python 学习笔记 3 ----> dive into python 3

    Python内置数据类型 注意: Python可以不需要声明变量的数据类型.它是根据变量的初始赋值情况分析数据类型,并在内部跟踪变量. 比较重要的数据类型: 1 布尔型(Booleans):True. ...

  9. python学习笔记(1)python中的注释和安装python

    注释 目标 注释的作用 单行注释 多行注释 01注释的作用 在程序中对代码的标注说明,增强代码的可读性 以 # 开头,# 右边的所有东西都被当做说明文字,而不是真正要执行的程序,只起到辅助说明作用 为 ...

  10. Python 学习笔记(十)Python集合(二)

    集合常用的方法 add()       向集合中增加一个元素,如果集合中已经有了这个元素,那个这个方法就会失效 >>> help(set.add) Help on method_de ...

随机推荐

  1. app中获取应用名称,版本等信息的方法

    在app中,我们有时候需要显示一些信息,例如名称,版本等等...如果用写死的方式可能不太好,我们可以动态的读取.应用的信息主要是在info.plist这个文件中,实际就是一个xml文件,以源文件的方式 ...

  2. 【IntelliJ Idea】idea下hibernate反向生成工具,根据数据表生成实体

    idea插件很齐全,不像ecplise一样.所以直接来步骤吧: 1.选择项目,右键-->Add Frameworks Support-->勾选Hibernate-->勾选Import ...

  3. ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock'

    原因:找不到mysql.sock文件 解决方法: 1 找到mysql.sock文件位置 echo "show variables" | mysql | grep "soc ...

  4. 版本控制工具:SVN和Maven的区别

    一.只有svn的情况 首先考虑没有maven的情况.这样的话,项目组每个开发人员,都需要在本地check out所有的源码. 每次提交之前,需要先更新周边工程的代码.由于工程之间是依赖的,所以很可能需 ...

  5. 用户空间和内核空间通讯之【Netlink 中】

    原文地址:用户空间和内核空间通讯之[Netlink 中] 作者:wjlkoorey258 今天我们来动手演练一下Netlink的用法,看看它到底是如何实现用户-内核空间的数据通信的.我们依旧是在2.6 ...

  6. 线程中的WaitForSingleObject和Event的用法

    http://chinaxyw.iteye.com/blog/548622 首先介绍CreateEvent是创建windows事件的意思,作用主要用在判断线程退出,程锁定方面. CreateEvent ...

  7. IO流中文件和文件夹的删除程序举例

    /* * 删除功能(无论是文件夹还是文件都是用delete方法,仅仅能一级一级的删除.):public boolean delete() * * 注意: * A:假设你创建文件或者目录忘了写盘符路径, ...

  8. 使用eclipse搭建maven多module项目(构建父子项目)

    创建空maven项目 File–>new–>project… 2.next 3.next 4.finish 5.配置pom.xml <project xmlns="http ...

  9. C 标准库 - <assert.h>

    C 标准库 - <assert.h> 简介 C 标准库的 assert.h头文件提供了一个名为 assert 的宏,它可用于验证程序做出的假设,并在假设为假时输出诊断消息. 已定义的宏 a ...

  10. BEGINNING SHAREPOINT&#174; 2013 DEVELOPMENT 第15章节--开发SP2013工作流应用程序

    BEGINNING SHAREPOINT® 2013 DEVELOPMENT 第15章节--开发SP2013工作流应用程序         本章节你将学到: SP中工作流的新功能: 理解工作流管理服务 ...