第零章 学前准备
第一章 数据结构 – 基本数据类型
第一章 数据结构 – 字符串
第一章 数据结构 – 列表、元组和切片
第一章 数据结构 – 字典
第一章 数据结构 – 集合
第一章 – 数组、队列、枚举
第一章 数据结构 – 序列分类
第二章 控制流程
第三章 函数也是对象 – 函数定义以及参数
第三章 函数也是对象 – 高阶函数以及装饰器
第三章 函数也是对象 – lambda 表达式、可调用函数及内置函数
第四章 面向对象编程 – 自定义类、属性、方法和函数
文章目录
第四章 面向对象编程 – 自定义类、属性、方法和函数
4.1 自定义类语法
- 如下定义步骤,类定义的关键字是
class
,一般类的名称方式使用大驼峰法命名方式命名,如下:FrenchDeck
,类名后面有小括号,指定父类,python
内基类是object
,默认所有类均继承object
。如果父类仅有object
类,可以省略继承的写法,比如:class FrenchDeck:
或class FrenchDeck():
- 每一个类中的方法第一个参数都是
self
,self
就是类对应的实例对象。
Card = collections.namedtuple('Card', ['rank', 'suit'])
class FrenchDeck(object):
ranks = [str(n) for n in range(2, 11)]+list('JQKA')
suits = 'spades diamonds clubs hearts'.split()
def __init__(self):
self._cards = [Card(rank, suit)
for suit in self.suits for rank in self.ranks]
def __len__(self):
return len(self._cards)
def __getitem__(self, position):
return self._cards[position]
4.2 属性
4.2.1 类属性
类属性其实就是定义在类中的变量。因为是定义在类中,所以,该类的所有实例共享类属性。
类属性的访问可以使用类名也可以使用实例访问。
4.2.2 实例属性
实例属性是类实例的变量马。因为是定义在类中,该类的所有实例单独管理自己的实例属性。
实例属性的访问只能使用实例访问,不能使用类名访问。
4.2.3 特殊属性
object.__dict__
:一个字典或其他类型的映射对象,用于存储对象的(可写)属性。instance.__class__
:类实例所属的类。class.__bases__
:由类对象的基类所组成的元组。class.__base__
:类的基类。definition.__name__
:类、函数、方法、描述器或生成器实例的名称。definition.__qualname__
:类、函数、方法、描述器或生成器实例的qualified name
。3.3 新版功能.class.__mro__
:此属性是由类组成的元组,在方法解析期间会基于它来查找基类。class.__subclasses__()
:每个类都存有对直接子类的弱引用列表。本方法返回所有存活引用的列表。列表的顺序按照子类定义的排列。
4.2.4 私有属性和"受保护的"属性
Python
不能像 Java
那样使用 private
修饰符创建私有属性,但是 Python
有个简单的机制,能避免子类意外覆盖“私有”属性。举个例子。有人编写了一个名为 Dog
的类,这个类的内部用到了 mood
实例属性,但是没有将其开放。现在,你创建了 Dog
类的子类: Beagle
。如果你在毫不知情的情况下又创建了名为 mood
的实例属性,那么在继承的方法中就会把 Dog
类的 mood
属性覆盖掉。这是个难以调试的问题。为了避免这种情况,如果以 __mood
的形式(两个前导下划线,尾部没有或最多有一个下划线)命名实例属性, Python
会把属性名存入实例的 __dict__
属性中,而且会在前面加上一个下划线和类名。因此,对 Dog
类来说, __mood
会变成 _Dog__mood
;对 Beagle
类来说,会变成 _Beagle__mood
。这个语言特性叫名称改写( name mangling
)。
Python
解释器不会对使用单个下划线的属性名做特殊处理,不过这是很多 Python
程序员严格遵守的约定,他们不会在类外部访问这种属性。遵守使用一个下划线标记对象的私有属性很容易,就像遵守使用全大写字母编写常量那样容易。Python文档的某些角落把使用一个下划线前缀标记的属性称为“受保护的”属性。使用 self._x
这种形式保护属性的做法很常见,但是很少有人把这种属性叫作“受保护的”属性。有些人甚至将其称为“私有”属性。
import doctest
class Person:
"""
>>> p = Person("xiaohong", "♀",18)
>>> p._Person__age
18
>>> p.__age
Traceback (most recent call last):
...
AttributeError: 'Person' object has no attribute '__age'
"""
def __init__(self, name, gender, age):
self.name = name
self.gender = gender
self.__age = age
doctest.testmod()
TestResults(failed=0, attempted=11)
4.3 方法和函数
4.3.1 实例方法
实例方法是操作实例的方法,传递的第一个参数是类的实例,参数名约定为 self
( 不强制为self
)。
4.3.2 类方法
定义操作类,而不是操作实例的方法。定义类方法需要使用装饰器 classmethod
。 classmethod
改变了调用方法的方式,因此类方法的第一个参数是类本身,而不是实例。 classmethod
最常见的用途是定义备选构造方法,因为操作的是类,可以通过类新建类的实例。按照约定,类方法的第一个参数名为 cls
(但是 Python
不介意具体怎么命名)。
4.3.2 静态方法
定义静态方法需要使用装饰器 staticmethod
。 staticmethod
装饰器也会改变方法的调用方式,但是第一个参数不是特殊的值。其实,静态方法就是普通的函数,只是碰巧在类的定义体中,而不是在模块层定义。
class Person:
quantity = 202178.37e9
def __init__(self, name, gender, age):
self.name = name
self.gender = gender
self.age = age
@classmethod
def from_id_card(cls, name, gender, age, id_card):
print(cls)
instance = cls(name, gender, age)
instance.id_card = id_card
return instance
@staticmethod
def number_add(n1, n2):
return n1+n2
def walk(self, speed):
print(self, f"move 速度{speed}迈")
def run(people, speed):
print(people, f"move 速度{speed}迈")
p1 = Person("xiaojuan", "♀", "18")
p2 = Person("xiaoyang", "♂", "17")
print('p1.quantity', p1.quantity, "Person.quantity", Person.quantity)
print(p1.name, p1.gender)
print(p2.name, p2.gender)
Person.quantity = p1.quantity//2
print(p2.quantity)
# 如果使用p1.quantity修改Person.quantity值呢?
p1.quantity = p1.quantity//2*2
print(p2.quantity)
print("*"*30)
p1.walk(80)
p1.run(180)
print("*"*30)
p3 = Person.from_id_card("xiaoshuai", "♂", "10", "10000000")
print(p3.id_card)
print(Person.number_add(1, 2))
p1.quantity 202178370000000.0 Person.quantity 202178370000000.0
xiaojuan ♀
xiaoyang ♂
101089185000000.0
101089185000000.0
******************************
<__main__.Person object at 0x00000162F3A77EB0> move 速度80迈
<__main__.Person object at 0x00000162F3A77EB0> move 速度180迈
******************************
<class '__main__.Person'>
10000000
3