实用Python程序设计MOOC-第十三章面向对象程序设计
[TOC]
实用Python程序设计MOOC-第十三章面向对象程序设计
面向对象程序设计
类和对象
为什么需要”类”
- 用列表或元组表示学生信息
1 | student = ["张三", 20001807, 3.4, "1988-01-24"] |
记不住GPA到底下标是多少
1 | student[3] = XXXX 也不知道是对哪项数据赋值 |
类和对象的概念
类是用来代表事物的。 对一种事物,可以设计一个类,概括出该种事物的属性,用成员变量表示之; 还要概括该种事物事物能进行的操作,用成员函数表示之。成员变量也称为类的“属性”,成员函数也称为类的“方法” 。
类的实例,称为“对象”。类代表一种事物的共同特点,对象就是一个具体的事物个体。
生成对象的方法: 类名(参数1,参数2……)
类的写法
1 | class 类名: |
矩形类示例
1 | class rectangle: |
类的作用
将数据和操作数据的函数捆绑在一起,便于当作一个整体使用
Python中的类
- 类型名即是类名:
float、str、list、dict …..
小数、复数、字符串、元组、列表、集合、字典等组合数据类型的常量,都是对象,函数也是对象,但整数型常量不是对象
各种库都是由类组成:
turtle、matplotlib、jieba、sqlite3
- 程序员可以自定义类,如rectangle
对象的比较
Python中所有的类,包括自定义的类,都有
__eq__
方法。x==y
的值,就是x.__eq__(y)
的值;如果x.__eq__(y)
没定义,那么就是y.__eq__(x)
的值。如果x.__eq__(y)
和y.__eq__(x)
都没定义,则x==y
也没定义(x,y都是整数常量时不适用)print(24.5.__eq__(24.5)) #>>True
a!=b
等价于 a.__ne__(b)
,或 b.__ne__(a)
(若a.__ne__(b)
没定义)
默认情况下, a.__ne__(b)
等价于not a.__eq__(b)
a<b
等价于 a.__lt__(b)
a>b
等价于 a.__gt__(b)
a<=b
等价于 a.__le__(b)
a>=b
等价于 a.__ge__(b)
自定义对象的比较
- 默认情况下,一个自定义类的
__eq__
方法,功能是判断两个对象的id是否相同。 - 默认情况下,一个自定义类的两个对象a和b,
a == b
和a is b
的含义一样,都是“a和b是否指向相同的地方”。同理,a != b
和not a is b
含义相同。 - 默认情况下,自定义类的对象不能比较大小,因其
__lt__
、__gt__
、__le__
、__ge__
方法都被设置成了None
对象比较大小程序示例
1 | class point: |
自定义类重写str方法可以将对象转字符串
1 | class point: |
继承和派生
要写小学生类、中学生类、大学生类….
所有学生都有共同点,每种学生又有各自特点,如何避免每个类都从头编写的重复劳动?
使用继承(派生)
定义一个新的类B时,如果发现类B拥有某个已写好的类A的全部特点,此外还有类A没有的特点,那么就不必从头重写类B,而是可以把A作为一个“基类”(也称“父类”),把B写为基类A的一个“派生类”(也称“子类”)来写。这样,就可以说从A类“派生”出了B类,也可以说B类“继承”了A类。
1 | class 类名(基类名): |
1 | import datetime |
1 | 输出: |
object类
- 所有类都是object类的派生类,因而具有object类的各种属性和方法
1 | class A: |
输出:
1 | ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__','__eq__', '__format__', '__ge__', '__getattribute__', '__gt__','__hash__','__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'func'] |
- 有的类
_lt__
,__gt__
等方法被设置成None,于是对象不可比较大小
静态属性和静态方法
- 静态属性被所有对象所共享,一共只有一份
- 静态方法不是作用在具体的某个对象上的,不能访问非静态属性
- 静态属性和静态方法这种机制存在的目的,就是为了少写全局变量和全局函数
静态方法不可以访问非静态属性。
静态方法和属性是用来替代全局变量和全局函数。
1 | class employee: |
对象作为集合元素或字典的键
什么是“可哈希”
可哈希的东西,才可以作为字典的键和集合的元素。
hash(x)
有定义,即为 x 可哈希hash(x) = x
(如果x是整型常量)hash(x) = x.__hash___()
(如果x不是整型常量)object类有
__hash__()
方法,返回值是个整数
列表、集合、字典的__hash__
成员函数都被设置成None,因此它们都不能成为集合的元素,或者字典的键,因为无法计算哈希值。
整数类型变量、小数、字符串、元组的哈希值,是根据它们的值算出来的,只要值相同,哈希值就相同。
1 | x = 23.1 |
哈希值和字典、集合的关系
字典和集合都是”哈希表”数据结构,根据元素的哈希值为元素找存放的”槽”,哈希值可以看作是槽编号。一个槽里面可以放多个哈希值相同的元素。
两个对象a,b若
hash(a) != hash(b)
,则a,b可以处于同一集合(也可以作为同一字典的不同元素的键)。两个对象a,b 若
hash(a) == hash(b)
,但a == b
不成立,则a,b可以处于同一集合(也可以作为同一字典的不同元素的键),即不算重复,可以放在同一个槽里。若 dt 是个字典,
dt[x]
计算过程如下:
1) 根据hash(x)去找x应该在的槽的编号;
2) 如果该槽没有元素,则认为dt中没有键为x的元素;
3) 如果该槽中有元素,则试图在槽中找一个元素y,使得y的键 == x
。如果找到,则dt[x]即为y的值,如果找不到,则dt[x]没定义,即认为dt中不存在键为x的元素。自定义类的对象,默认情况下哈希值是根据对象id进行计算。所以两个对象,只要
a is b
不成立, a和b的哈希值就不同,就可以同时存在于一个集合内,或作为同一字典的不同元素的键。可以重写自定义类的
__hash__()
方法,使得对象的哈希值和对象的值,而不是id相关,这样值相同的对象,就不能处于同一个集合中,也不能作为同一字典不同元素的键。
1 | class A: |
自定义类的对象是否可哈希
a==b
等价于a.__eq__(b)
。自定义类的默认__eq__
函数是判断两个对象的id是否相同。自定义类的默认__hash__
函数是根据对象id算哈希值的。如果为自定义的类重写了
__eq__(self,other)
成员函数,则其__hash__
成员函数会被自动设置为None。这种情况下,该类就变成不可哈希的一个自定义类,只有在重写了
__eq__
方法却没有重写__hash__
方法的情况下,才是不可哈希的。
自定义类重写__hash___
但不重写__eq__
1 | class A: |
自定义类同时重写__hash___
和__eq__
1 | class A: |