1修改实例的字符串表示

可以通过定义__str__()和__repr__()方法来实现

class Pair:
def __init__(self,x,y):
self.x = x
self.y = y def __str__(self):
return "(%s,%s)"%(self.x,self.y) def __repr__(self):
return "Pair(%s,%s)"%(self.x,self.y) p = Pair(2,5)
print(p)
print("p is {0!r}".format(p))

对于__repr__(),标准的方法是让他产生的字符串文本能够满足eval(repr(x)) == x

__str__()则产生一段有意义的文本

2自定义字符串的输出格式

我们想让对象通过format()函数和字符串方法来支持自定义的输出格式

要自定义字符串的输出格式,可以在类中定义__format__()方法

_formats = {
"ymd":"{d.year}-{d.month}-{d.day}",
"mdy":"{d.month}/{d.day}/{d.year}",
"dmy":"{d.day}/{d.month}/{d.year}"
} class Date:
def __init__(self,year,month,day):
self.year = year
self.month = month
self.day = day def __format__(self,code):
if code == "":
code = "ymd"
fmt = _formats[code]
return fmt.format(d = self) d = Date(2018,9,26)
print(format(d))
print(format(d,"dmy"))
print(format(d,"mdy"))

3让对象支持上下文管理协议

我们想让对象支持上下文管理协议,即可以通过with语句触发。

想让对象支持上下文管理协议,对象需实现__enter__()和__exit__()方法,比如实现网络连接的类。

from socket import socket,AF_INET,SOCK_STREAM

class LazyConnection:

    def __init__(self,address,family = AF_INET, type = SOCK_STREAM):
self.address = address
self.family = family
self.type = type
self.sock = None def __enter__(self):
if self.sock is not None:
raise RuntimeError("Already connected")
self.sock = socket(self.family,self.type)
self.sock.connect(self.address)
return self.sock def __exit__(self, exc_type, exc_val, exc_tb):
self.sock.close()
self.sock = None conn = LazyConnection("www.baidu.com") with conn as s:
s.send(b'hahhahah')

4当创建大量实例时如何节省内存

当我们的程序需要创建大量的实例(百万级),这样会占用大量的内存。

#对于那些主要用作简单数据结构的类,通常可以在类定义中增加__slot__属性,以此来大量减少对内存的使用。

class Date:
__slots__ = ["year","month","day"] def __init__(self,year,month,day):
self.year = year
self.month = month
self.day = day

当定义了__slots__属性时,python会采用一种更加紧凑的内部表示,会将实例的属性添加到一个小型数组里,不再为每个实例创建__dict__。

副作用是我们不能为实例添加新的属性。是一种优化手段

5将名称封装到类中

在python中,以单下划线_开头的属性被认为是一种私有属性

class A:
def __init__(self):
self._name = "jiaojianglong"
self.age = 24 def _internal_method(self):
print("i am a internal method") a = A()
print(a._name) #jiaojianglong

python并不会阻止访问属性,但编译器不会做提示。如果强行访问会被认为是粗鲁的。

在类的定义中也见到过双下划线__开头的名称,以双下划线开头的名称会导致出现名称重组的行为

class B:
def __init__(self):
self.__name = "jiaojianglong" b = B()
# print(b.__name)#AttributeError: 'B' object has no attribute '__name'
print(b._B__name)#jiaojianglong

这样的行为是为了继承,以双下划线开头的属性不会被子类通过继承而覆盖。

class C(B):
def __init__(self):
super().__init__() c = C()
print(c._B__name)#jiaojianglong

大部分情况下我们使用单下划线,涉及到子类继承覆盖的问题时使用双下划线

当我们想定义一个变量,但是名称可能会与保留字段冲突,基于此,我们在名称后加一个单下划线以示区别。lambda_

6创建可管理的属性

#在对实例的获取和设定上,我们希望增加一些额外的处理过程。

class Person:
def __init__(self,first_name):
self.first_name = first_name @property
def first_name(self):
return self._first_name @first_name.setter
def first_name(self,value):
if not isinstance(value,str):
raise TypeError("Excepted a string")
self._first_name = value p = Person("jiao")
print(p.first_name)

在创建实例时,__inti__()中我们将name赋值到self.first_name,实际会调用setter方法,所以name实际还是储存在self._first_name中

7调用父类中的方法

#我们想调用一个父类中的方法,这个方法在子类中已经被覆盖了。

class A:
def spam(self):
print("A.spam") class B(A):
def spam(self):
print("B.spam")
super().spam() b = B().spam()#B.spam,A.spam print(B.__mro__)#(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)

争对每一个类,python都会计算出一个称为方法解析顺序(MRO)的列表,MOR列表只是简单的对所有的基类进行线性排列。

8在子类中扩展属性

我们想在子类中扩展某个属性的功能,而这个属性是在父类中定义的

9创建一种新形式的类属性或实例属性

如果想定义一种新形式的实例属性,可以以描述符的形式定义其功能。

class Integer():

    def __init__(self,name):
self.name = name def __get__(self, instance, owner):
if instance is None:
return self
else:
return instance.__dict__[self.name] def __set__(self, instance, value):
if not isinstance(value,int):
raise TypeError("Expected an int")
instance.__dict__[self.name] = value def __delete__(self, instance):
del instance.__dict__[self.name] class Point:
x = Integer("x")
y = Integer("y") def __init__(self,x,y):
self.x = x
self.y = y p = Point(2,3)
print(p.x)#
p.y = 5
print(p.y)#
# p.x = "a"#TypeError: Expected an int
print(Point.x)#<__main__.Integer object at 0x00000141E2ABB5F8>

__get__()方法看起来有些复杂的原因是实例变量和类变量的区别,如果是类变量则简单的返回描述符本身,如果是实例变量返回定义的值

关于描述符,常容易困惑的地方就是他们只能在类的层次上定义,不能根据实例来产生,下面的代码是无法工作的

class Point:

    def __init__(self,x,y):
self.x = Integer("x")
self.y = Integer("y")
self.x = x
self.y = y p = Point(2,"c")
print(p.x)#
print(p.y)#c class Typed:
def __init__(self,name,expected_type):
self.name = name
self.expected_type = expected_type def __get__(self, instance, owner):
if instance is None:
return self
else:
return instance.__dict__[self.name] def __set__(self, instance, value):
if not isinstance(value,self.expected_type):
raise TypeError("Expected %s"%self.expected_type)
instance.__dict__[self.name] = value def __delete__(self, instance):
del instance.__dict__[self.name] def typeassert(**kwargs):
def decorate(cls):
for name,expected_type in kwargs.items():
setattr(cls,name,Typed(name,expected_type))
return cls
return decorate @typeassert(name=str,shares = int,price=float)
class Stock:
def __init__(self,name,shares,price):
self.name = name
self.shares = shares
self.price = price

对于少量的定制还是使用property简单些,如果是大量的定制则使用描述符要简单些

cookbook_类与对象的更多相关文章

  1. Java编程里的类和对象

    像我们搞计算机这块的,都知道这么一件事,当前的计算机编程语言主要分为两大块,一为面向过程,二为面向对象.Java就是一门纯面向对象的语言.学习了一个月左右的Java,在下对于Java当中的类和对象有了 ...

  2. Python - 类与对象的方法

    类与对象的方法

  3. C++基础知识(5)---类和对象

    终于把C++中的基础在前面的几篇博客中总结完了,可能还有一些语法还没有总结到,没关系,以后用到了再查资料就好.类是C++中的一个非常重要的概念,这是区别你使用的C++到底是面向过程还是面向对象的一个重 ...

  4. 简述JavaScript对象、数组对象与类数组对象

    问题引出 在上图给出的文档中,用JavaScript获取那个a标签,要用什么办法呢?相信第一反应一定是使用document.getElementsByTagName('a')[0]来获取.同样的,在使 ...

  5. 前端学PHP之面向对象系列第一篇——类和对象

    × 目录 [1]类 [2]成员属性[3]成员方法[4]对象[5]成员访问[6]this 前面的话 面向对象程序设计(OOP)是一种计算机编程架构.计算机程序由单个能够起到子程序作用的单元或对象组成,为 ...

  6. Objective-C Runtime 运行时之一:类与对象

    Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理.这种动态语言的优势在于:我们写代码时更具灵活性,如我们可以把消息转发给我们想要的对象,或者随意交换一 ...

  7. [Java入门笔记] 面向对象编程基础(一):类和对象

    什么是面向对象编程? 我们先来看看几个概念: 面向过程程序设计 面向过程,是根据事情发展的步骤,按进行的顺序过程划分,面向过程其实是最为实际的一种思考方式,可以说面向过程是一种基础的方法,它考虑的是实 ...

  8. 解析Java类和对象的初始化过程

    类的初始化和对象初始化是 JVM 管理的类型生命周期中非常重要的两个环节,Google 了一遍网络,有关类装载机制的文章倒是不少,然而类初始化和对象初始化的文章并不多,特别是从字节码和 JVM 层次来 ...

  9. 02OC的类和对象

    这章重点介绍OC的类以及对象,由于C语言是面向过程语言,而OC只是对于C语言多了一些面向对象的特性,所以OC相对于其他面向对象语言,例如C#.Java等没有那么多的语法特性,所以差别还是比较大的. 一 ...

随机推荐

  1. 给你的 GitHub Repository 加上 sponsor 按钮

    「本文微信公众号 AndroidTraveler 首发」 背景 其实之前 GitHub 就已经说过要给开源的开发者提供赞助支持. 当你进入 GitHub 主页时,你会在右边发现一个 Tips. 点击进 ...

  2. SQL Server Alwayson架构下 服务器 各虚拟IP漂移监控告警的功能实现 -2(虚拟IP视角)

    1.需求描述 我们知道Windows Cluster 都是多节点的,当虚拟IP漂移的时候,一般都是从一个节点漂移到另外一个节点.如果可以及时捕捉到旧节点信息是什么.新节点信息是什么对我们提供高可用的数 ...

  3. Zookeeper详解-安装(四)

    ZooKeeper服务器是用Java创建的,它在JVM上运行.你需要使用JDK 6或更高版本. 步骤1:验证Java安装 相信你已经在系统上安装了Java环境.现在只需使用以下命令验证它. $ jav ...

  4. Rails.cache相关知识

    可能里面的一些知识已经不被大家使用了,但是作为学习,我想和大家分享一下个人关于Rails.cache的浅显的认识,望大家指教. 1.Rails.cache是什么 它是Rails中的缓存,拥有所有缓存的 ...

  5. Scala 学习之路(三)—— 流程控制语句

    一.条件表达式if Scala中的if/else语法结构与Java中的一样,唯一不同的是,Scala中的if表达式是有返回值的. object ScalaApp extends App { val x ...

  6. WebService跨域配置、Ajax跨域请求、附开发过程源码

    项目开发过程中需要和其他公司的数据对接,当时我们公司提供的是WebService,本地测试,都是好的,Ajax跨域请求,就报错,配置WebService过程中,花了不少功夫,入不少坑,不过最终问题还是 ...

  7. 浅入深出Vue:事件处理

    上一篇的最后留下了一个 v-on的思考,也就是本章的主题:事件处理 为什么需要事件处理 在前端开发中,经常要面对各种表单.按钮.而这里面就住着一个事件:点击 (click). 前端童鞋们肯定不陌生它, ...

  8. 汇编入门三-CPU工作原理

    本文为读书笔记,个人总结与摘抄自<汇编语言 第二版> 1.CPU从内存中读取数据,首先要获得存储单元的地址. 2.指明进行的操作,如存储或者读写 所以,CPU要进行操作总结为: 1.存储单 ...

  9. Xmanager 5远程连接CentOS7图形化界面

    1.安装Xmanager 5下载链接:https://pan.baidu.com/s/1JwBk3UB4ErIDheivKv4-NA提取码:cw04 双击xmgr5_wm.exe进行安装 点击‘下一步 ...

  10. gulp使用详情 及 3.0到4.0的坑

    项目的所有依赖都可以安装,每个都有详细的注释. const gulp = require('gulp'); const sass = require('gulp-sass'); const brows ...