Python 中的类与继承

这篇具有很好参考价值的文章主要介绍了Python 中的类与继承。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

类的定义以及实例的建立

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...'

Python 中的类与继承

像这样,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中,函数也是对象,对象和函数的区别并不显著。

喜欢的觉得有用的就点个赞吧,点波关注不迷路呦文章来源地址https://www.toymoban.com/news/detail-426928.html

到了这里,关于Python 中的类与继承的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • PHP8的类与对象的基本操作之类的实例化-PHP8知识详解

    定义完类和方法后,并不是真正创建一个对象。类和对象可以描述为如下关系。类用来描述具有相同数据结构和特征的“一组对象”,“类”是“对象”的抽象,而“对象”是“类”的具体实例,即一个类中的对象具有相同的“型”,但其中每个对象却具有各不相同的“值”

    2024年02月08日
    浏览(49)
  • 13.Netty源码之Netty中的类与API

    Bootstrap 意思是引导,一个 Netty 应用通常由一个 Bootstrap 开始,主要作用是配置整个 Netty 程序,串联各个组件,Netty 中ServerBootstrap 是服务端启动引导类。 java //泛型 AbstractBootstrapB extends AbstractBootstrapB, C, C extends Channel ​ ServerBootstrap extends AbstractBootstrapServerBootstrap, ServerChannel ​

    2024年02月15日
    浏览(53)
  • MFC中的类继承图的基本框架

    一、类继承关系 从图中可知,在MFC中大多数的类都派生于CObject类,它的主要作用是为子类提供一些基本的功能,这些派生类构成了MFC应用程序的基本框架,它们各自的功能描述如表1所示。 派生类 功能描述 CCmdTarget 用于处理用户请求 CWinThread 代表应用程序内部的执行线程

    2024年02月09日
    浏览(38)
  • 什么是JavaScript中的类(Class)和继承(Inheritance)?它们与原型链继承相比有哪些优缺点?

    JavaScript中的类(Class)和继承(Inheritance)是面向对象编程的重要概念。类是一种抽象的概念,它定义了一组属性和方法,并且这些属性和方法可以在同一组对象中重复使用。继承是类之间的层次关系,其中一个类继承了另一个类的属性和方法,从而能够共享其代码和行为。 在Ja

    2024年02月12日
    浏览(58)
  • 【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解

    🏠专栏介绍:浅尝C++专栏是用于记录C++语法基础、STL及内存剖析等。 🎯每日格言:每日努力一点点,技术变化看得见。 我们生活中也有继承的例子,例如:小明继承了孙老师傅做拉面的手艺。继承就是一种延续、复用的方式。C++为了提高代码的可复用性,引入了继承机制,

    2024年04月10日
    浏览(48)
  • Python框架【模板继承 、继承模板实战、类视图 、类视图的好处 、类视图使用场景、基于调度方法的类视图】(四)

    👏作者简介:大家好,我是爱敲代码的小王,CSDN博客博主,Python小白 📕系列专栏:python入门到实战、Python爬虫开发、Python办公自动化、Python数据分析、Python前后端开发 📧如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀 🔥如果感觉博主的文章还不错的

    2024年02月11日
    浏览(51)
  • typeScript中的类以及类的限制

    传统方法中,JavaScript通过构造函数实现类的概念,通过原型链实现继承。而在Es6中,我们终于迎来了class. typeScript除了实现了所有Es6中类的功能以外,还添加了一些新的用法。 虽然 JavaScript 中有类的概念,但是可能大多数 JavaScript 程序员并不是非常熟悉类,这里对类相关的概

    2024年02月05日
    浏览(37)
  • 【C++的类与对象(下)】

    我们都知道构造函数的作用是在创建对象的时候给对象的成员属性赋值。 那么下面的场景可以用构造函数进行赋值吗? 答案是不可以的。编译器也不支持这么胡搞。 首先要知道为什么不可以这么写。 为了解决上面不能使用构造函数来初始化的场景,所以就引入了初始化列表

    2024年02月03日
    浏览(38)
  • Python趣味入门12:初遇类与实例

    小牛叔用轻松有趣的故事,带你进入Python的编程世界。 一提到类大神们就经常说封装。说白了,封装即把围绕同一个对象相同的代码、数据整合在一起。比如在某段游戏代码中(比如熊猫厨房),有一个“面包”: 1、游戏提供“烘烤”的操作。 - 很明显这是动作 - 类的方法

    2024年02月04日
    浏览(50)
  • unity C#中的封装、继承和多态简单易懂的经典实例

    C#中的封装、继承和多态是面向对象编程(OOP)的三大核心特性。下面分别对这三个概念进行深入解释,并通过实例来说明它们在实际开发中的应用。 封装 (Encapsulation) 实例1: 在这个例子中, BankAccount 类封装了账户余额(balance),外部不能直接修改它,只能通过 Deposit 和

    2024年02月22日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包