Class

类的定义以及实例的建立

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

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

  1. class Person(object):
  2. pass

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

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

  1. zhangsan=Person()
  2. lisi=Person()

实例的属性及方法

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

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

__init__()

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

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

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

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

  1. class Person(object):
  2. def __init__(self, name, gender, birth):
  3. self.name = name
  4. self.gender = gender
  5. self.birth = birth

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

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

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

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

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

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

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

法被外部访问。例如:

  1. class Person(object):
  2. def __init__(self, name):
  3. self.name = name
  4. self._title = 'Mr'
  5. self.__job = 'Student'
  6.  
  7. p1=Person("zhangsan")
  8.  
  9. print(p1.name,'\n')
  10. print(p1._title,'\n')
  11. try:
  12. print(p1.__job)
  13. except:
  14. print("Error")

结果:

  1. zhangsan
  2.  
  3. Mr
  4.  
  5. Error

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

实例的方法

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

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

  1. class Person(object):
  2.  
  3. def __init__(self, name):
  4. self.__name = name
  5.  
  6. def get_name(self):
  7. return self.__name
  8.  
  9. p2=Person("Bob")
  10. print(p2.get_name())

结果

  1. Bob

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

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

python中实例方法也是属性

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

  1. import types
  2. def Pn_get_name(self):
  3. return str("This person's name is :"+self.name)
  4.  
  5. class Person(object):
  6. def __init__(self, name):
  7. self.name = name
  8.  
  9. p1=Person("Alice")
  10. p2=Person("Bob")
  11.  
  12. p1.get_name=types.MethodType(Pn_get_name,p1)
  13.  
  14. print(p1.get_name(),'\n')
  15. print(p2.get_name())

结果:

  1. This person's name is :Alice
  2.  
  3. Traceback (most recent call last):
  4. File "D:/pythonwork/practise/test.py", line 15, in <module>
  5. print(p2.get_name())
  6. AttributeError: 'Person' object has no attribute 'get_name'

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

类的属性及方法

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

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

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

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

  1. class Person(object):
  2. address = 'Earth'
  3. def __init__(self, name):
  4. self.name = name
  5.  
  6. p1=Person("zhangsan")
  7.  
  8. print(Person.address)
  9. print(p1.address)

结果:

  1. Earth
  2. Earth

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

  1. Person.address = 'China'
  2.  
  3. print(Person.address)
  4. print(p1.address)

结果:

  1. China
  2. China

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

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

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

  1. p1.address="China"
  2.  
  3. print(Person.address)
  4. print(p1.address)

结果:

  1. Earth
  2. China

类的方法

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

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

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

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

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

  1. class Person(object):
  2. count = 0
  3. @classmethod
  4. def how_many(cls):
  5. return cls.count
  6.  
  7. def __init__(self, name):
  8. self.name = name
  9. Person.count = Person.count + 1
  10.  
  11. print (Person.how_many())
  12. p1 = Person('Bob')
  13. print (Person.how_many())

结果:

  1. 0
  2. 1

类的继承

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

  1. class Person(object):
  2. def __init__(self, name, gender):
  3. self.name = name
  4. self.gender = gender
  5.  
  6. class Student(Person):
  7. def __init__(self, name, gender, score):
  8. super(Student, self).__init__(name, gender)
  9. self.score = score

注意:

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

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

python中的多态

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

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

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

例如:

  1. class Person(object):
  2. pass
  3. def Pstr(self):
  4. print("This is Person class method")
  5.  
  6. class Student(Person):
  7. def __init__(self, name):
  8. self.name = name
  9. def Pstr(self):
  10. print("This is Student class method")
  11.  
  12. class Teacher(Person):
  13. def __init__(self, name):
  14. self.name = name
  15.  
  16. p1=Student("Alice")
  17. p2=Teacher("Tom")
  18.  
  19. p1.Pstr()
  20. p2.Pstr()

结果:

  1. This is Student class method
  2. This is Person class method

python中的多重继承

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

例如:

  1. class A(object):
  2. def __init__(self, a):
  3. print 'init A...'
  4. self.a = a
  5.  
  6. class B(A):
  7. def __init__(self, a):
  8. super(B, self).__init__(a)
  9. print 'init B...'
  10.  
  11. class C(A):
  12. def __init__(self, a):
  13. super(C, self).__init__(a)
  14. print 'init C...'
  15.  
  16. class D(B, C):
  17. def __init__(self, a):
  18. super(D, self).__init__(a)
  19. print 'init D...'

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

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

运行测试代码:

  1. d=D('d')

结果:

  1. init A...
  2. init C...
  3. init B...
  4. init D...

类的定制

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

python中 __slots__

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

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

例如:

  1. class Student(object):
  2. __slots__ = ('name', 'gender', 'score')
  3. def __init__(self, name, gender, score):
  4. self.name = name
  5. self.gender = gender
  6. self.score = score

进行操作:

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

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

python中 __str__和__repr__对于print的改变

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

  1. class Student(object):
  2. def __init__(self, name):
  3. self.name = name
  4.  
  5. p1=Student("Alice")
  6.  
  7. print(p1)

结果:

  1. <__main__.Student object at 0x000002672671EF98>

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

  1. class Student(object):
  2. def __init__(self, name):
  3. self.name = name
  4. def __str__(self):
  5. return 'Student: %s' % self.name
  6.  
  7. p1=Student("Alice")
  8.  
  9. print(p1)

结果:

  1. Student: Alice

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

  1. >>>p1
  2. <Student object at 0x0000023C8F331A58>

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

用于显示给开发人员。

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

  1. __repr__ = __str__

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

  1. >>>p1
  2. Student: Alice

python3中__cmp__方法已经不再使用

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

 

python中 __len__对于len()的改变

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

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

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

  1. class Students(object):
  2. def __init__(self, *args):
  3. self.names = args
  4. def __len__(self):
  5. return len(self.names)
  6.  
  7. ss = Students('Bob', 'Alice', 'Tim')
  8. print (len(ss))

结果:

  1. 3

类的实例的数学运算

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

__add__对符号 + 进行改变

__sub__对符号 -进行改变

__mul__对符号 * 进行改变

__div__对符号 / 进行改变

例如:

我们创建一个分数的类:

  1. class Rational(object):
  2. def __init__(self, p, q):
  3. self.p = p
  4. self.q = q

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

  1. class Rational(object):
  2. def __init__(self, p, q):
  3. self.p = p
  4. self.q = q
  5. def __add__(self, r):
  6. return Rational(self.p * r.q + self.q * r.p, self.q * r.q)
  7. def __str__(self):
  8. return '%s/%s' % (self.p, self.q)
  9. __repr__ = __str__
  10.  
  11. r1 = Rational(1, 3)
  12. r2 = Rational(1, 2)
  13. print(r1+r2)

结果:

  1. 5/6

类的实例的类型转换

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

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

例如__int__:

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

结果:

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

python中 @property

对于Student类:

  1. class Student(object):
  2. def __init__(self, name, score):
  3. self.name = name
  4. self.score = score

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

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

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

如果利用两个方法:

  1. class Student(object):
  2. def __init__(self, name, score):
  3. self.name = name
  4. self.__score = score
  5. def get_score(self):
  6. return self.__score
  7. def set_score(self, score):
  8. if score < 0 or score > 100:
  9. raise ValueError('invalid score')
  10. self.__score = score

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

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

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

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

  1. class Student(object):
  2. def __init__(self, name, score):
  3. self.name = name
  4. self.__score = score
  5. @property
  6. def score(self):
  7. return self.__score
  8. @score.setter
  9. def score(self, score):
  10. if score < 0 or score > 100:
  11. raise ValueError('invalid score')
  12. self.__score = score

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

  1. s = Student('Bob', 59)
  2. s.score = 60
  3. print (s.score)
  4. try:
  5. s.score = 1000
  6. except:
  7. print("Error")

结果:

  1. 60
  2. Error

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

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

python中 __call__

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

  1. >>> f = abs
  2. >>> f.__name__
  3. 'abs'
  4. >>> f(-123)
  5. 123

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

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

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

  1. class Person(object):
  2. def __init__(self, name, gender):
  3. self.name = name
  4. self.gender = gender
  5.  
  6. def __call__(self, friend):
  7. print ('My name is %s...' % self.name)
  8. print ('My friend is %s...' % friend)

现在可以对 Person 实例直接调用:

  1. >>> p = Person('Bob', 'male')
  2. >>> p('Tim')
  3. My name is Bob...
  4. My friend is Tim...

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

python中的类与继承的更多相关文章

  1. 关于Python中的类普通继承与super函数继承

    关于Python中的类普通继承与super函数继承 1.super只能用于新式类 2.多重继承super可以保公共父类仅被执行一次 一.首先看下普通继承的写法 二.再看看super继承的写法 参考链接 ...

  2. Python 中的类的继承

    class parent(object): def override1(self): print("Parent") class child(parent): def overri ...

  3. GO-结构体(类似python中的类)

    一.结构体有名属性 package main import "fmt" func main(){ type Sb struct { name string //属性名,数据类型 a ...

  4. Python中的类、对象、继承

    类 Python中,类的命名使用帕斯卡命名方式,即首字母大写. Python中定义类的方式如下: class 类名([父类名[,父类名[,...]]]): pass 省略父类名表示该类直接继承自obj ...

  5. python中的类和实例

    今天花了两个多小时后搜索相关博客看了看python中有关类和实例的介绍,差不多大概明白了. python中的类和c++中的类是一样的,不同之处就是c++的类,如果含有成员变量,并且成员变量发生变化后, ...

  6. 4、Python中的类详解(0601)

    <大话数据结构>的作者程杰在博客园也有博客,网址是:http://cj723.cnblogs.com/ 面向对象编程(OOP) 1.程序 = 指令 + 数据 代码可以选择以指令为核心或以数 ...

  7. python中新式类和经典类

    python中的类分为新式类和经典类,具体有什么区别呢?简单的说, 1.新式类都从object继承,经典类不需要. Python 2.x中默认都是经典类,只有显式继承了object才是新式类 Pyth ...

  8. Python中的类(一)

    Python中的类(一) 一. 应用场景 如果多个函数中有一些相同的参数时,转换成面向对象. 二. 如何创建类 类是用来描述具有相同的属性和方法的对象的集合.它定义了该集合中每个对象所共有的属性和方法 ...

  9. python中元类(metaclass)的理解

    原文地址:http://www.cnblogs.com/tkqasn/p/6524879.html 一:类也是对象 类就是一组用来描述如何生成一个对象的代码. 类也是一个对象,只要你使用关键字clas ...

随机推荐

  1. SVN Commit报错 svn: E155037: Previous operation has not finished; run 'cleanup' if it was interrupted

    svn commit 文件出错 svn: E155037: Commit failed (details follow): svn: E155037: Previous operation has n ...

  2. 如何解决bib的一些问题

    胡老师留的大作业要求综述,因而有很多文献引用.但是当使用bibtex的方法,特别是中文文献的引用会遇到一些问题. 网上相关的解答有: http://blog.sciencenet.cn/blog-10 ...

  3. 为什么在3ds Max 按系统默认的快捷键AIT+W 视口最大化切换没反应?

    经常使用3ds Max快捷键做效果图可谓是提高作图速度的毕竟之路,做效果图的朋友或许会遇到最大化视口显示失灵的问题,max默认快捷键为Alt+W,这里我不建议大家更改max的默认快捷键,当然Max也提 ...

  4. vue下assets下的静态资源和static下的静态资源的区别

    区别一(最终位置) assets文件是src下的,所以最后运行是需要进行打包,而static文件不需要打包直接放在最终的文件中了 区别二(引用方式) assets中的文件在vue中的template/ ...

  5. NOIp2018模拟赛四十

    今天太晚了...题解到时候补吧(flag立好) 成绩:100+0+0=100 感觉A题本质暴力贪心?C题一道水题我居然没做...亏爆 A:[agc011e]increasing numbers B:[ ...

  6. Vue 基础篇

    Vue 基础篇 一.框架与库的区别 JQ库->DOM(DOM操作) + Ajax请求 art-template库->模板引擎 框架 -> 全方位.功能齐全 简易的DOM体验 + 发请 ...

  7. list 分页

    package com.jsz.peini.common.util; import java.util.ArrayList; import java.util.List; public class S ...

  8. 常用Java开源库(新手必看)

    Jakarta common: Commons LoggingJakarta Commons Logging (JCL)提供的是一个日志(Log)接口(interface),同时兼顾轻量级和不依赖于具 ...

  9. mysql中文乱码解决方式

    近期项目使用到mysql.却突然出现了中文乱码问题.尝试了多种方案,最终解决乱码问题,总结一下解决方式,给遇到同样问题的人一点參考. 中文乱码的原因 1.安装mysqlserver的时候编码集设定有问 ...

  10. 【剑指Offer学习】【面试题47:不用加减乘除做加法】

    题目:写一个函数,求两个整数之和,要求在函数体内不得使用+.-.×.÷四则运算符号. 解题思路 5 的二进制是101, 17 的二进制是10001 .还是试着把计算分成三步:第一步各位相加但不计进位. ...