Python 中的类与继承

news2024/11/25 8:19:52

类的定义以及实例的建立

Python中,类通过 class 关键字定义。

例如最简单的一个类定义可以为:

class Person(object):
    pass

Python 的编程习惯,类名以大写字母开头,紧接着是(object),表示该类是从哪个类继承下来的。

例如上面的person类,我们可以创建很多实例:

zhangsan=Person()
lisi=Person()

实例的属性及方法

Python是动态语言,对每一个实例,都可以直接给他们的属性赋值,即使类中并无定义,例如,给xiaoming这个实例加上name、gender和birth属性:

xiaoming = Person()
xiaoming.name = 'Xiao Ming'
xiaoming.gender = 'Male'
xiaoming.birth = '1990-1-1'

__init__()

如上所说,我们可以自由地给一个实例绑定各种属性,但是,现实世界中,一种类型的实例应该拥

有相同名字的属性。例如,Person类应该在创建的时候就拥有 name、gender 和 birth 属性。

所以在创建类时,我们可以使用__init__()方法,当创建实例时,__init__()方法被自动调用,我们就

能在此为每个实例都统一加上以下属性:

class Person(object):
    def __init__(self, name, gender, birth):
        self.name = name
        self.gender = gender
        self.birth = birth

__init__() 方法的第一个参数必须是 self(也可以用别的名字,但建议使用习惯用法),后续参数则

可以自由指定,和定义函数没有任何区别。

同时,创建实例时,就必须要提供除 self 以外的参数:

xiaoming = Person('Xiao Ming', 'Male', '1991-1-1')
xiaohong = Person('Xiao Hong', 'Female', '1992-2-2')

属性的访问限制(私有属性)

我们可以给一个实例绑定很多属性,如果有些属性不希望被外部访问到怎么办?

Python对属性权限的控制是通过属性名来实现的,如果一个属性由双下划线开头(__),该属性就无

法被外部访问。例如:

class Person(object):
    def __init__(self, name):
        self.name = name
        self._title = 'Mr'
        self.__job = 'Student'

p1=Person("zhangsan")

print(p1.name,'\n')
print(p1._title,'\n')
try:
    print(p1.__job)
except:
    print("Error")

结果:

zhangsan 

Mr 

Error

可以看出只有__job无法被访问

实例的方法

虽然私有属性无法从外部访问,但是,从类的内部是可以访问的。除了可以定义实例的属性外,还可以定义实例的方法。

例如我们可以定义一个__name私有属性,同时创建一个get_name方法去访问__name:

class Person(object):

    def __init__(self, name):
        self.__name = name

    def get_name(self):
        return self.__name

p2=Person("Bob")
print(p2.get_name())

结果

Bob

get_name(self) 就是一个实例方法,它的第一个参数是self。__init__(self, name)其实也可看做是一个特殊的实例方法。

在实例方法内部,可以访问所有实例属性,这样,如果外部需要访问私有属性,可以通过方法调用获得,这种数据封装的形式除了能保护内部数据一致性外,还可以简化外部调用的难度。

python中实例方法也是属性

我们在 class 中定义的实例方法其实也是属性,它实际上是一个函数对象,因为方法也是一个属性,所以,它也可以动态地添加到实例上,只是需要用 types.MethodType() 把一个函数变为一个方法:

import types
def Pn_get_name(self):
    return str("This person's name is :"+self.name)

class Person(object):
    def __init__(self, name):
        self.name = name

p1=Person("Alice")
p2=Person("Bob")

p1.get_name=types.MethodType(Pn_get_name,p1)

print(p1.get_name(),'\n')
print(p2.get_name())

结果:

This person's name is :Alice 

Traceback (most recent call last):
  File "D:/pythonwork/practise/test.py", line 15, in <module>
    print(p2.get_name())
AttributeError: 'Person' object has no attribute 'get_name'

给一个实例动态添加方法并不常见,直接在class中定义要更直观

类的属性及方法

类是模板,而实例则是根据类创建的对象。绑定在一个实例上的属性不会影响其他实例,但是,类本身也是一个对象,如果在类上绑定一个属性,则所有实例都可以访问类的属性,并且,所有实例访问的类属性都是同一个,也就是说,实例属性每个实例各自拥有,互相独立,而类属性有且只有一份。

定义类属性可以直接在 class 中定义:

class Person(object):
    address = 'Earth'
    def __init__(self, name):
        self.name = name

因为类属性是直接绑定在类上的,所以,访问类属性可以直接用类名访问,也可以使用实例访问,例如:

class Person(object):
    address = 'Earth'
    def __init__(self, name):
        self.name = name

p1=Person("zhangsan")

print(Person.address)
print(p1.address)

结果:

Earth
Earth

由于Python是动态语言,类属性也是可以动态添加和修改的:

Person.address = 'China'

print(Person.address)
print(p1.address)

结果:

China
China

因为类属性只有一份,所以,当Person类的address改变时,所有实例访问到的类属性都改变了。

注意:如果在实例变量上修改类属性,则实际上是给实例绑定了一个与类属性同名的实例属性,而

访问时会优先访问实例属性:

p1.address="China"

print(Person.address)
print(p1.address)

结果:

Earth
China

类的方法

和属性类似,方法也分实例方法和类方法。

在class中定义的全部是实例方法,实例方法第一个参数 self 是实例本身。

要在class中定义类方法,我们通过标记一个 @classmethod,该方法将绑定到 Person 类上,而非

类的实例。类方法的第一个参数将传入类本身,通常将参数名命名为 cls,下面例子中

的 cls.count 实际上相当于 Person.count。

class Person(object):
    count = 0
    @classmethod
    def how_many(cls):
        return cls.count

    def __init__(self, name):
        self.name = name
        Person.count = Person.count + 1

print (Person.how_many())
p1 = Person('Bob')
print (Person.how_many())

结果:

0
1

类的继承

如果已经定义了Person类,需要定义新的Student和Teacher类时,可以直接从Person类继承,定义Student类时,只需要把额外的属性加上,例如score:

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

class Student(Person):
    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score

注意:

一定要用 super(Student, self).__init__(name, gender) 去初始化父类,否则,继承自 Person 的 Student 将没有 name 和 gender。

函数super(Student, self)将返回当前类继承的父类,即 Person ,然后调用__init__()方法,注意self参数已在super()中传入,在__init__()中将隐式传递,不需要写出(也不能写),另外下方会写出其他的写法。

初始化父类的多种写法

class AAA(object):
    def __init__(self, name):
        self.name = name

    def get_name(self):
        print(self.name)
        return self.name


class BBB(AAA):
    def __init__(self, age):
        self.age = age
        # 方式1
        super().__init__(name='BBB')
        # 方式2
        super(BBB, self).__init__(name='BBBC')
        # 方式3
        AAA.__init__(self, name='BBBD')

    def get_age(self):
        print(self.age)
        return self.age

其中方法3适用于多继承写法,python中如果两个父类的非继承自同一个“爷类” super()初始化似乎只会初始继承的第一个父类,因此这种情况,需要用第三种方式编写(也或许是我理解有问题,编码过程中具体问题具体分析)

class AAA():
    def __init__(self, name):
        self.name = name

    def get_name(self):
        print(self.name)
        return self.name


class BBB():
    def __init__(self, name2):
        self.name2 = name2

    def get_name2(self):
        print(self.name2)
        return self.name2


class CCC(AAA, BBB):
    def __init__(self, age):
        self.age = age
        AAA.__init__(self, name='CCC')
        BBB.__init__(self, name2='CCC2')

    def get_age(self):
        print(self.age)
        return self.age


TEST = CCC(2)
TEST.get_name()
TEST.get_name2()

python中的多态

类具有继承关系,并且子类类型可以向上转型看做父类类型,如果我们从 Person 派生出 Student和

Teacher ,并都写了一个 同名的whoAmI() 方法,当我们调用实例的whoAmI() 方法时,总是先查找

它自身的定义,如果没有定义,则顺着继承链向上查找,直到在某个父类中找到为止。

例如:

class Person(object):
    pass
    def Pstr(self):
        print("This is Person class method")

class Student(Person):
    def __init__(self, name):
        self.name = name
    def Pstr(self):
        print("This is Student class method")

class Teacher(Person):
    def __init__(self, name):
        self.name = name

p1=Student("Alice")
p2=Teacher("Tom")

p1.Pstr()
p2.Pstr()

结果:

This is Student class method
This is Person class method

python中的多重继承

除了从一个父类继承外,Python允许从多个父类继承,称为多重继承。

例如:

class A(object):
    def __init__(self, a):
        print 'init A...'
        self.a = a

class B(A):
    def __init__(self, a):
        super(B, self).__init__(a)
        print 'init B...'

class C(A):
    def __init__(self, a):
        super(C, self).__init__(a)
        print 'init C...'

class D(B, C):
    def __init__(self, a):
        super(D, self).__init__(a)
        print 'init D...'

像这样,D 同时继承自 B 和 C,也就是 D 拥有了 A、B、C 的全部功能。多重继承通过 super()调用

__init__()方法时,A 虽然被继承了两次,但__init__()只调用一次:

 运行测试代码:

d=D('d')

结果:

init A...
init C...
init B...
init D...

类的定制

我们可以使用一些特殊的方法来定制我们自己定义的类

python中 __slots__

由于Python是动态语言,任何实例在运行期都可以动态地添加属性。

如果要限制添加的属性,例如,Student类只允许添加 name、gender和score 这3个属性,就可以利用Python的一个特殊的__slots__来实现。

例如:

class Student(object):
    __slots__ = ('name', 'gender', 'score')
    def __init__(self, name, gender, score):
        self.name = name
        self.gender = gender
        self.score = score

进行操作:

>>> s = Student('Bob', 'male', 59)
>>> s.name = 'Tim' # OK
>>> s.score = 99 # OK
>>> s.grade = 'A'
Traceback (most recent call last):
  ...
AttributeError: 'Student' object has no attribute 'grade'

__slots__的目的是限制当前类所能拥有的属性,如果不需要添加任意动态的属性,使用__slots__也能节省内存。

python中 __str__和__repr__对于print的改变

例如原本对于类中的实例直接打印:

class Student(object):
    def __init__(self, name):
        self.name = name

p1=Student("Alice")

print(p1)

结果:

<__main__.Student object at 0x000002672671EF98>

但是我们想让print实例的时候直接打印类名与实例名我们就可以使用__str__:

class Student(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return 'Student: %s' % self.name

p1=Student("Alice")

print(p1)

结果:

Student: Alice

但是我们在命令交互行中直接输入变量p1却不是这样:

>>>p1
<Student object at 0x0000023C8F331A58>

这是因为 Python 定义了__str__()和__repr__()两种方法,__str__()用于显示给用户,而__repr__()

用于显示给开发人员。

同理我们可以定义类的__repr__()例如直接:

__repr__ = __str__

这样直接在命令交互中直接输入变量p1也是同样效果了

>>>p1
Student: Alice

python3中__cmp__方法已经不再使用

在python2中该方法可实现对于sorted()函数改变,不再赘述

python中 __len__对于len()的改变

如果一个类表现得像一个list,要获取有多少个元素,就得用 len() 函数。

要让 len() 函数工作正常,类必须提供一个特殊方法__len__(),它返回元素的个数。

例如,我们写一个 Students 类,把名字传进去:

class Students(object):
    def __init__(self, *args):
        self.names = args
    def __len__(self):
        return len(self.names)

ss = Students('Bob', 'Alice', 'Tim')
print (len(ss))

结果:

3 

类的实例的数学运算

同以上特殊方法一样,我们可以利用

__add__对符号 + 进行改变

__sub__对符号 -进行改变

__mul__对符号 * 进行改变

__div__对符号 / 进行改变

例如:

我们创建一个分数的类:

class Rational(object):
    def __init__(self, p, q):
        self.p = p
        self.q = q

我们想实现符号+直接运用在我们创建的实例上我们需要这样写代码:

class Rational(object):
    def __init__(self, p, q):
        self.p = p
        self.q = q
    def __add__(self, r):
        return Rational(self.p * r.q + self.q * r.p, self.q * r.q)
    def __str__(self):
        return '%s/%s' % (self.p, self.q)
    __repr__ = __str__

r1 = Rational(1, 3)
r2 = Rational(1, 2)
print(r1+r2)

结果:

5/6

类的实例的类型转换

如上,Rational类实现了有理数(分数)类的数学运算,但是,如果要把结果转为 int 或 float 怎么办?

我们对于整数和浮点数可以直接使用int()和float()函数直接转换,但是我们肯定无法直接使用在我们所创建的类的实例上,因此我们需要使用__int__()和__float__()

例如__int__:

class Rational(object):
    def __init__(self, p, q):
        self.p = p
        self.q = q
    def __int__(self):
        return self.p//self.q   #python3中//是整数结果

结果:

>>> print int(Rational(7, 2))
3

python中 @property

对于Student类:

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score

当我们想要修改一个 Student 的 scroe 属性时,可以这么写:

s = Student('Bob', 59)
s.score = 1000

显然,直接给属性赋值无法检查分数的有效性。

如果利用两个方法:

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.__score = score
    def get_score(self):
        return self.__score
    def set_score(self, score):
        if score < 0 or score > 100:
            raise ValueError('invalid score')
        self.__score = score

这样一来,s.set_score(1000) 就会报错。

这种使用 get/set 方法来封装对一个属性的访问在许多面向对象编程的语言中都很常见。但是写 s.get_score() 和 s.set_score() 没有直接写 s.score 来得直接。

而在Python中,python支持高阶函数,在函数式编程中我们介绍了装饰器函数,可以用装饰器函数@property。

把 get/set 方法“装饰”成属性调用:

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.__score = score
    @property
    def score(self):
        return self.__score
    @score.setter
    def score(self, score):
        if score < 0 or score > 100:
            raise ValueError('invalid score')
        self.__score = score

现在再设置score会怎么样呢:

s = Student('Bob', 59)
s.score = 60
print (s.score)
try:
    s.score = 1000
except:
    print("Error")

结果:

60
Error

注意: 第一个score(self)是get方法,用@property装饰,第二个score(self, score)是set方法,用@score.setter装饰,@score.setter是前一个@property装饰后的副产品。说明对 score 赋值实际调用的是 set方法。

如果没有定义set方法即@score.setter,就不能对“属性”赋值,这时,就可以创建一个只读“属性”。

python中 __call__

在Python中,函数其实是一个对象:

>>> f = abs
>>> f.__name__
'abs'
>>> f(-123)
123

由于 f 可以被调用,所以,f 被称为可调用对象。而所有的函数都是可调用对象。

一个类实例也可以变成一个可调用对象,只需要实现一个特殊方法__call__()。

我们把 Person 类变成一个可调用对象:

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

    def __call__(self, friend):
        print ('My name is %s...' % self.name)
        print ('My friend is %s...' % friend)
>>> p = Person('Bob', 'male')
>>> p('Tim')
My name is Bob...
My friend is Tim...

单看 p('Tim') 你无法确定 p 是一个函数还是一个类实例,所以,在Python中,函数也是对象,对象和函数的区别并不显著。

喜欢的觉得有用的就点个赞吧,点波关注不迷路呦

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/47063.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决单文件组件里的跨域请求数据问题(使用vue单文件组件请求数据必会遇到的问题!!!)

为什么要解决跨域问题&#xff1a; 因为浏览器有限制&#xff0c;只有同域名同端口号下的数据才能拿来用&#xff1b;那如果想拿到不同域名不同端口号下的数据就不行了&#xff1b; 在单文件组件中如何去解决跨域问题&#xff1a; 因为服务器没有跨域限制&#xff0c;只有浏览…

PyTorch(四)Torchvision 与 Transforms

文章目录Log一、Torchvision1. CIFAR10① 介绍② 使用2. 与 Transforms 结合使用总结Log 2022.11.28接着开启新的一章2022.11.29继续学习 一、Torchvision 视频教程中 Torchvision v0.9.0文档Torchvision 官方文档Torchvision Datasets API 文档 1. CIFAR10 ① 介绍 CIFAR…

HBuilder X实现banner轮播图

第一步还是去仔细阅读官方文档&#xff0c;找到组件下面的内置组件里面的swiper&#xff0c;如图所示 官方是提供了一个用来制作轮播图的滑块视图容器&#xff1a;swiper 一般来说&#xff0c;轮播图的图片地址是由后端返回给前端遍历显示在页面上的&#xff0c;所以基本的结构…

世界杯的“中国元素”昂扬大国担当,点面科技全新推出的多模态多功能移动终端踏上卡塔尔征途!

本次世界杯中国足球队没有参加&#xff0c;但是在世界杯的赛场上到处都有中国元素。 中国承建的卢赛尔体育场 卡塔尔世界杯主体育场——卢赛尔球场由中国铁建以总承包身份承建&#xff0c;引发了全球瞩目。在本届世界杯赛事中&#xff0c;该场馆将承担包括决赛在内的10场比赛&…

idea手动创建webapp(在main文件夹下)

SSM自学笔记 文章目录一、Maven使用正常情况首先不使用骨架创建好Maven项目然后选择Project Structure...选择要创建webapp的模块修改路径二、Maven不正常工作时一、Maven使用正常情况 首先不使用骨架创建好Maven项目 然后选择Project Structure… 选择要创建webapp的模块 选好…

前置微小信号放大器在光声技术的血管识别研究中的应用

实验名称&#xff1a;前置微小信号放大器在光声技术的血管识别研究中的应用 研究方向&#xff1a;生物识别技术 测试目的&#xff1a; 利用MATLAB对光声血管进行识别&#xff1a;1、对光声血管图库的图像进行预处理包括归一化、二值化、平滑、细化和毛刺修剪得到细化图像&#…

0109 蓝桥杯真题12

/* * 观察下面的现象,某个数字的立方&#xff0c;按位累加仍然等于自身。 * 1^3 1 * 8^3 512 5128 * 17^3 4913 491317 ... *请你计算包括1,8,17在内&#xff0c;符合这个性质的正整数一共有多少个&#xff1f; *填写该数字&#xff0c;不要填写任何多余的内…

【DevPress】V2.4.3版本发布,增加内容收录管理

DevPress V2.4.3版本于2022年11月10日发版&#xff0c;增加内容收录模块&#xff0c;方便用户内容收录。 一、该版本功能包含 1、新需求 1&#xff09;控制台增加内容收录管理模块&#xff0c;包括收录内容额度管理、自动收录功能、基于内容搜索做收录以及收录内容列表。 - …

凯利公式 - Kelly formula

欢迎关注沉睡者IT&#xff0c;点上面关注我 ↑ ↑ 凯利公式的概述 凯利公式是一条可应用在投资资金和赌注的公式。应用于多次的随机赌博游戏&#xff0c;资金的期望增长率最高&#xff0c;且永远不会导致完全损失所有资金的后果。它假设赌博可无限次进行&#xff0c;而且没有下…

Netty进阶——粘包与半包(滑动窗口)

TCP 以一个段&#xff08;segment&#xff09;为单位&#xff0c;每发送一个段就需要进行一次确认应答&#xff08;ack&#xff09;处理&#xff0c;但如果这么做&#xff0c;缺点是包的往返时间越长性能就越差。 为了解决此问题&#xff0c;引入了窗口概念&#xff0c;窗口大小…

微慕积分商城插件

微信小程序专业版在第一个正式版发布的时候&#xff0c;就推出了积分系统。用户登录小程序、签到、发表文章、评论文章、发表话题、回复话题、点赞、赞赏、付费阅读后都可以获得相应的积分。但这个积分攒了足够多后&#xff0c;有什么用呢&#xff1f;可以用于积分阅读文章&…

论文代码测试

一、代码所属论文 1. Document Image Enhancement with GANs - TPAMI journal 代码中的介绍提供了处理四种情况的模型&#xff0c;这四种模型功能分别为 &#xff1a; 1&#xff09;二值化图片文字; 2&#xff09;清晰化图片中模糊文字; 3&#xff09;去除图片水印; 4&…

FPGA----ZCU106基于axi-hp通道的pl与ps数据交互(全网唯一最详)

1、大家好&#xff0c;今天给大家带来的内容是&#xff0c;基于AXI4协议的采用AXI-HP通道完成PL侧数据发送至PS侧&#xff08;PS侧数据发送至PL侧并没有实现&#xff0c;但是保留了PL读取PS测数据的接口&#xff09; 2、如果大家用到SoC这种高级功能&#xff0c;那大家应该对于…

系列ML.Net 学习篇【一】——初识机器学习

由于公司项目涉及到机器学习和图像识别,虽然我并不是算法专家&#xff0c;但毕竟需要了解和知道其运转原理&#xff0c;因此自我进行了学习进化&#xff0c;决定在机器学习上有所进展&#xff0c;结合.NET技术的ML.NET,把机器学习的技能提升一个Level&#xff0c;如果你也有兴趣…

vue学习31~39(列表过滤+列表排序+vue检测data中的数据+收集表单数据+过滤器)

1.14 列表过滤 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-wid…

VM虚拟机三种网络配置详解(桥接、NAT、仅主机)

在学习网络配置之前&#xff0c;需要搞明白有关于网络的一些基本概念&#xff0c;什么是ip&#xff0c;子网掩码&#xff0c;网段&#xff0c;网关&#xff1f;这里只做最简单的解释&#xff0c;实际上远远不止这些。 网络名词 ip 在现实生活中&#xff0c;这么大的一个中国…

bootstrap标签和导航条件组件学习(七)

标签和导航条件组件 这里的标签是不能切换的&#xff0c;它是静态的&#xff0c;在JavaScrapt插件里面是是可以切换页面的 需要配合插件 加选中状态&#xff1a; 它是自适应屏幕宽度&#xff1a; 胶囊式标签下拉菜单&#xff1a; 复制代码放到页面中&#xff1a; div 的class…

KubeEdge设备接入的mqtt调试

kubeedge的设备接入是用mqtt进行转接的。但是如果遇到一些问题需要排查问题时&#xff0c;希望可以查看终端发的mqtt消息。 目前 mosquitto 没有看到有进行消息查看的机制&#xff08;也可能我不知道&#xff09; EMQ可以进行消息查看。 1.安装EMQ 快速开始 | EMQX 5.0 文…

数据存储——声音存储

声音数字化一、声音的采样&#xff08;一&#xff09;信号分类1.模拟信号2. 数字信号&#xff08;二&#xff09;存储1、采样2.量化3.编码1.数字音频的技术指标2.未经压缩的数字音频的数据量&#xff08;字节&#xff08;8bit&#xff09;&#xff09;总结 声音的数字化过程一、…

[附源码]SSM计算机毕业设计校园一卡通管理信息系统台JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…