前言
python完全采用面向对象程序设计的思想,完全支持面向对象的基本功能,如面向对象编程三大特性封装、继承、多态等。python中对象很广泛。python中的一切内容都可以称为对象。
一、类的定义与使用
格式
class 类名(父类):
def func(self,方法所需参数):
pass
说明:
-
定义的类默认继承object。
-
实例化对象后可以通过对象.成员的方式访问其中的数据成员或方法。
class Phone():
def infor(self,type):
pass
huawei=Phone()#实例化
huawei.infor('huawei')
- self参数:类的所有实例方法都必须有self参数且必须在最前面。self表示对象参数本身。在类中访问实例属性或者实例方法都需要以self.为前缀。但在外部是不需要的。
class Piont():
def add(self):
print(id(self))
t=Piont()
t.add()#2030765052544
print(id(t))#2030765052544
- cls参数:cls表是这个类本身。
类成员与实例成员
这里成员指的是数据成员或者广义上的属性。属性分为两种:
- 实例属性,指的是在__init__中定义的,定义和使用必须以self.为前缀。
- 类属性则类似于像函数中一定义的变量。或者说静态字段
实例属性只可以通过对象名访问,类属性可以通过对象名访问也可以通过类来访问。
class Car:
price=200#类属性
def __init__(self,color):
self.color=color#实例属性
carprice=Car.price#200
#carcolor=Car.color#AttributeError: type object 'Car' has no attribute 'color'
car1=Car('white')
car1.price#200
car1.color#white
私有成员和公成员
将私有数据封装到类的实例上,但是又需要考虑到python对属性缺乏访问控制手段。这时候可以用到私有成员过不成文的命名规则
- 1.以单下划线(_)开头命名应该属于内部实现,不能被模糊导入。
- 2.双下划线开头命名,以这种方式命名会出现”名称重整”【_类名__成员】。以前两者方式命名的东西不建议去直接访问,其实python中不存在严格意义上的私有成员。
class B:
def __init__(self):
self.private=1#共有属性
self.__private=0#_B_private
def __func(self):#被重整为_B_func
pass
def func1(self):
self.__func()
b=B()
b.__private#AttributeError: 'B' object has no attribute '__private'
b._B__private
方法
方法可以粗略的分为四大类:公有方法、私有方法、类方法、静态方法。
- 公有方法:如上func1法
- 私有方法:如上__func方法,命名与私有成员
- 静态方法:上加@staticmethod。静态方法参数不必须,可以通过类调用。
- 类方法:
@classmethod
def classmd(cls)【cls,是类名】
【当需求与对象无关时,可以用类方法或静态方法】
方法还可分为普通方法和魔术方法
魔术方法
当满足某个条件时,自动触发的方法。在Python中,所有以“__”双下划线包起来的内置方法,都统称为“Magic Method” 魔术方法。
- __init__当一个实例被创建的时候调用的初始化方法.创建实例所需要的参数往往就在这定义;
- new 是在一个对象实例化的时候所调用的第一个方法。通过这个方法可以不用通过__init__来创建实例(不推荐)
class Date:
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
data={'year':2012,'month':8,'day':30}
d=Date.__new__(Date)
for key,value in data.items():
setattr(d,key,value)
print(d.year)#2012
- str()和__repr__()
修改实例的字符串表示:默认的实例打印输出为地址,我们想要打印输出更具有意义。
我可以设定__str__()【转化为字符串时调用】和__repr__()【打印、转化时调用】方法来实现。
class Pair():
def __init__(self,x,y):
self.x=x
self.y=y
def __repr__(self):
return 'Pair({},{})'.format(self.x,self.y)
def __str__(self):
return '({},{})'.format(self.x,self.y)
p=Pair(3,4)
print(p)#(3,4)
- format()
自定义字符串的输出格式,我们想让对象通过format()函数和字符串方法来支持自定义的输出格式。可以在类定以__format__()方法
_formats={
'ymd':'{d.year}-{d.month}-{d.day}',
'mdy':'{d.month}-{d.day}-{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]
print(fmt.format(d=self))
return fmt.format(d=self)
#调用format方法
d=Date(2020,12,21)
format(d)#2020-12-21
format(d,'mdy')#12-21-2020
- enter()和__exit__()
让对象支持上下文管理协议(with语句):要让对象能够兼容需要实现__enter__()和__exit__()方法,当遇到with语句enter方法首先触发执行,其返回值被放置在as的限定变量中。最后执行exit方法
from socket import socket,AF_INET,SOCK_STREAM
class LazyConnection:
def __init__(self,adress,family=AF_INET,type=SOCK_STREAM):
self.address=adress
self.family=family
self.type=type
self.sock=None
def __enter__(self):
if self.sock is not None:
raise RuntimeError('Already connencted')
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
from functools import partial
conn=LazyConnection(('www.python.org',80))
with conn as s:
s.send(b'GET /index.html HTTP/1.0 \r\n')
s.send(b'Host:www.python.org\r\n')
s.send(b'\r\n')
resp=b''.join(iter(partial(s.recv,8192),b''))
- 其他方法
setitem安照索引赋值
getitem安照索引取值
len
....
等其他方法
继承
继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。是为了代码复用设计的.
class Father:#父类;基类
...
class Son(Father)#子类,派生类
...
子类可以在继承父类方法的同时,可以对其方法进行重构。这样一来,子类的方法既包含父类方法的特性,同时也包含子类自己的特性,私有成员不能继承原因上面已经给出.
调用父类方法
- super().方法
- 类名.方法
使用第二种在多继承时,往往会造成多次调用的结果,但更加直观。
多继承
关于同名方法查找顺序:自己–深度优先(从左到右),共同最后.
高级属性
此处讲的属性不是前面谈到的属性,在对实例属性的获取和设定上,希望增加一些额外的处理过程(类型检测等)或者进行只读保护即需要对属性得访问之类得操作进行自定义。一种简单的方法是将其定义为property【把类中定义的函数当做一种属性来使用】,property属性实际上就是把一系列的方法绑定在一起。通过对属性操作捕获来进行自动触发。
class Person:
def __init__(self,firat_name):
self.first_name=firat_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("Expected a string")
self._first_name=value
@first_name.deleter
def first_name(self):
raise AttributeError("Can't delete")
a=Person("Jhon")
a.first_name#Jhon
# a.first_name=12#TypeError: Expected a string
del a.first_name #AttributeError: Can't delete
使用这种方法不要编写定义了大量重复性的property的代码,因为不够优雅。或者可以利用描述符或闭包来完成。
如果需要在子类中扩展某个属性的功能,而这个属性是在父类中定义的.
class SubPerson(Person):
@property
def first_name(self):
print('GET Name')
return super().first_name
@first_name.setter
def first_name(self,value):
print('Setting name to',value)
super(SubPerson, SubPerson).first_name.__set__(self,value)
@first_name.deleter
def first_name(self):
print('Del first_name')
super(SubPerson,SubPerson).first_name.__delete__()
s=SubPerson("Jhon")
s.first_name
s.first_name=12
如果只想扩展其中的某个方法可以
class SubPerson1(Person):
@Person.first_name.getter
def first_name(self):
print('GET Name')
return super().first_name
@Person.first_name.setter
def first_name(self,value):
print('Setting name to',value)
super(SubPerson, SubPerson).first_name.__set__(self,value)
描述符类
上面的property在本质上就是提供了一个内置得描述符类型,它知道如何将一个属性链接到一组方法上。描述符允许你自定义在引用一个对象属性时应该完成的事。描述符类:用__get__【获取】、set【设置】、delete【del】方法的形式实现的三个属性核心操作的类。实现了前两者的描述符被称为数据描述符,实现__get__描述符被称为非数据描述符(如函数对象)。
class Intger:
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=Intger('x')
y=Intger('y')
def __init__(self,x,y):
self.x=x
self.y=y
要使用一个描述符类,我们把描述符的实例当作类变量,当这样做所有针对描述符类的访问都会被其捕获。值得一提的是数据描述符优于__dict__查找,而非数据描述符则劣于__dict__查找
委托
一种编程模式,将特定的操作转交给另一个不同的对象。访问实例的属性时,能够将其委托到一个内部持有的对象上,可以作为继承的替代方案或者为了实现一种代理机制。
在这里插入代码片class A:
def spam(self):
pass
class B:
def __init__(self):
self._a=A()
def spam(self):
return self._a.spam()
有许多方法都需要委托,可以在被委托函数中定义__getattr__()方法【访问的属性不存在时,调用】
class A:
def spam(self):
return 'A'
class B:
def __init__(self):
self._a=A()
# def spam(self):
# return self._a.spam()
def __getattr__(self, item):
return getattr(self._a,item)
b=B()
print(b.spam())#A
说明:getattr()以字符串的形式直接调用对象上的方法。
class Piont():
def __init__(self,x,y):
self.x=x
self.y=y
def distance(self,x,y):
print(x,y)
return x+y
d=getattr(Piont,'distance')
print(d)
槽
__solts__
创建大量实例时节省内存:创建大量实例时为节省内存我们可以在类中定义__solts__属性设置一个静态属性列表,并在类的实例中跳过__dict__字典的创建过程,以此来减少对内存空间的使用。
class Date:
__slots__ = ['year','month','day']
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
具体原理不在此处赘述,这种方法有很大限制只能定义__slots__中列出的属性名。
多态
python中不考虑多态
二、需求示例
- 简化数据结构初始化
以下将是示例,先定义一个基类,其他类继承即可
class Structure():
_fields=[]
def __init__(self,*args):
if len(args)!=len(self._fields)
raise TypeError('Too longer or Too short')
for name,value in zip(self._fields,args):
setattr(self,name,value)
class Stock(Structure):
_fields = ['x','y']
参考书籍:python高级编程、python食谱、python程序设计等