python类

本文详细介绍了Python中的类定义与使用,包括格式、成员类型、私有成员、方法(如魔术方法)、继承、多态等概念。通过实例展示了如何定义类、创建对象,以及使用描述符、委托和槽等高级特性。此外,还讨论了Python中的多态特性及其应用场景。
摘要由CSDN通过智能技术生成

前言

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” 魔术方法。

  1. __init__当一个实例被创建的时候调用的初始化方法.创建实例所需要的参数往往就在这定义;
  2. 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
  1. 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)
  1. 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
  1. 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''))
  1. 其他方法
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中不考虑多态

二、需求示例

  1. 简化数据结构初始化
    以下将是示例,先定义一个基类,其他类继承即可
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程序设计等

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值