发布于2019-08-05 18:21 阅读(488) 评论(0) 点赞(2) 收藏(5)
可以通过定义__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__()则产生一段有意义的文本
我们想让对象通过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"))
我们想让对象支持上下文管理协议,即可以通过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')
当我们的程序需要创建大量的实例(百万级),这样会占用大量的内存。
#对于那些主要用作简单数据结构的类,通常可以在类定义中增加__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__。
副作用是我们不能为实例添加新的属性。是一种优化手段
在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_
#在对实例的获取和设定上,我们希望增加一些额外的处理过程。
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中
#我们想调用一个父类中的方法,这个方法在子类中已经被覆盖了。
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列表只是简单的对所有的基类进行线性排列。
我们想在子类中扩展某个属性的功能,而这个属性是在父类中定义的
如果想定义一种新形式的实例属性,可以以描述符的形式定义其功能。
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)#2 p.y = 5 print(p.y)#5 # 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)#2 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简单些,如果是大量的定制则使用描述符要简单些
作者:阿里妈妈
链接:https://www.pythonheidong.com/blog/article/6200/db6b12ec2d7fbd5860a2/
来源:python黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 python黑洞网 All Rights Reserved 版权所有,并保留所有权利。 京ICP备18063182号-1
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!