- 类(Class)
- 类定义与实例化
- 数据属性
- 方法对象与函数
- 类变量与实例变量
- 继承
- 多重继承
- 类方法与静态方法
- 常用内置函数
- abs()、hex()、divmod()、max()、min()
- getattr()、hasattr()、setattr()
- chr()、ord()
- zip()
- 标准库
- PyPI(cheeseshop)
这是本系列最后一篇,介绍类与内置函数,以及简单提及标准库与PyPI。到此,作为一门语言,Python 的主要内容都基本介绍完了,但是要真正用 Python 干一些奇妙的事,还需要继续学习。
类(Class)
关于 类 的详细教程请参考:https://docs.python.org/zh-cn/3/tutorial/classes.html 。另外,在之前的文章里提到过 python 的类没有真正的 私有属性。
类定义与实例化与函数定义 (def 语句) 一样,类定义必须先执行才能生效。
当一个类定义了 __init__() 方法时,类的实例化操作会自动为新创建的类实例发起调用 __init__()。
>>> class Complex: ... def __init__(self, realpart, imagpart): ... self.r = realpart ... self.i = imagpart ... >>> x = Complex(3.0, -4.5) >>> x.r, x.i (3.0, -4.5)数据属性
数据属性(Data Attribute)即实例变量,数据属性不需要声明,像局部变量一样,它们将在第一次被赋值时产生。
>>> class MyClass: ... pass ... >>> x.counter = 1 >>> while x.counter < 10: ... x.counter = x.counter * 2 ... >>> print(x.counter) 16方法对象与函数
下面代码中,MyClass.foo 是一个函数,而 x.foo 是一个方法对象。方法是从属于“对象”的函数。方法的特殊之处在于实例对象会作为函数的第一个参数被传入。 在下面的示例中,调用 x.foo() 其实就相当于 MyClass.foo(x)。
调用一个具有 n 个参数的方法就相当于调用再多一个参数的对应函数,这个参数值为方法所属实例对象,位置在其他参数之前。
方法的第一个参数常常被命名为 self。 这也不过就是一个约定: self 这一名称在 Python 中绝对没有特殊含义。 但是要注意,不遵循此约定会使得你的代码对其他 Python 程序员来说缺乏可读性 …
>>> class MyClass:
... def foo(self):
... print('Hello world.')
...
>>> x = MyClass()
>>> x.foo()
Hello world.
>>> MyClass.foo(x)
Hello world.
实例方法对象也具有属性: foo.__self__ 就是带有 foo() 方法的实例对象,而 foo.__func__ 则是该方法所对应的函数对象。
>>> x = MyClass() >>> x.foo.__self__ <__main__.MyClass object at 0x000001DBBA66F3A0> >>> x.foo.__self__ is x True >>> x.foo.__func__ is MyClass.foo True类变量与实例变量
一般来说,实例变量用于每个实例的唯一数据,而类变量用于类的所有实例共享的属性和方法。
class Dog:
kind = 'canine' # 类变量
def __init__(self, name):
self.name = name # 实例变量
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.kind # shared by all dogs
'canine'
>>> e.kind # shared by all dogs
'canine'
>>> d.name # unique to d
'Fido'
>>> e.name # unique to e
'Buddy'
共享数据可能在涉及 mutable 对象例如列表和字典的时候导致令人惊讶的结果。例如以下代码中的 tricks 列表不应该被用作类变量,因为所有的 Dog 实例将只共享一个单独的列表:
class Dog:
tricks = [] # mistaken use of a class variable
def __init__(self, name):
self.name = name
def add_trick(self, trick):
self.tricks.append(trick)
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks # unexpectedly shared by all dogs
['roll over', 'play dead']
正确的类设计应该使用实例变量:
class Dog:
def __init__(self, name):
self.name = name
self.tricks = [] # creates a new empty list for each dog
def add_trick(self, trick):
self.tricks.append(trick)
如果同样的属性名称同时出现在实例和类中,则属性查找会优先选择实例:
>>> class Person: ... name = 'John' ... >>> a = Person() >>> a.name = 'Tom' >>> b = Person() >>> print(a.name, b.name) Tom John
每个值都是一个对象,因此具有 类 (也称为 类型),并存储为 object.__class__ 。
>>> a.__class__继承>>> 'Hello'.__class__ >>> (1, 2, 3).__class__ >>> int('1').__class__
作为面向对象的语言,Python当然支持继承。
class SubClass(ClassA): # 继承自 ClassA
...
在C++中,有三个概念:Overload、Override、Overwrite:
- Overload 是指同一个类中多个同名函数拥有不同参数,叫做 重载;
- Override 是指派生类中定义了与基类 名称相同且参数相同 的 virtual 函数,叫做 覆盖;
- Overwrite 是指派生类中的函数在 同名不同参 等情况下隐藏了基类同名函数的情况,叫做 改写。
具体可参考:C++中的Overload、Override和Overwrite
Python类中,所有的方法都是 virtual 方法,即可以被 “覆盖”。基类方法如果调用了同一基类中定义的另一方法,最终可能会调用覆盖它的派生类的方法。例如:
class ClassA:
def foo(self):
print("foo in class A.")
def bar(self):
print("bar in class A.")
self.foo() # 调用实例方法 foo()
class SubClass(ClassA):
def foo(self):
print("foo in sub-class.")
>>> x = SubClass()
>>> x.foo()
foo in sub-class.
>>> x.bar()
bar in class A.
foo in sub-class.
Python解析对象的属性引用时遵循一定的顺序,首先在当前类中查找,如果找不到再递归地在基类中查找。以下代码的表现类似于C++中的 Overwrite。
class ClassA:
def foo(self):
print("foo in class A.")
def bar(self):
print("bar in class A.")
self.foo()
class SubClass(ClassA):
def foo(self):
print("foo in sub-class.")
def bar(self, message): # 改写了 bar(),增加了 message 参数
super().bar()
print("bar in sub-class:", message)
>>> x = SubClass()
>>> x.bar('Hello') # 调用子类方法成功
bar in class A.
foo in sub-class.
bar in sub-class: Hello
>>> x.bar() # 调用基类方法失败
TypeError("bar() missing 1 required positional argument: 'message'")
>>> ClassA.bar(x) # 调用基类方法成功
bar in class A.
foo in sub-class.
isinstance() 与 issubclass() 函数:
>>> print(isinstance(x, SubClass)) True >>> print(isinstance(x, ClassA)) True >>> print(issubclass(ClassA, ClassA)) True >>> print(issubclass(SubClass, ClassA)) True多重继承
Python 支持多重继承。多重继承的语法很简单,例如:
class SubClass(ClassA, ClassB) # 继承自 ClassA 与 ClassB
...
多重继承的一个关键问题是:当调用 super().foo() 时,到底调用的是哪个基类的方法?
对于多数应用来说,在最简单的情况下,你可以认为搜索从父类所继承属性的操作是 深度优先、从左至右 的,当层次结构中存在重叠时不会在同一个类中搜索两次。 因此,如果某一属性在 DerivedClassName 中未找到,则会到 Base1 中搜索它,然后(递归地)到 Base1 的基类中搜索,如果在那里未找到,再到 Base2 中搜索,依此类推。
真实情况比这个更复杂一些;方法解析顺序会动态改变以支持对 super() 的协同调用。 这种方式在某些其他多重继承型语言中被称为后续方法调用,它比单继承型语言中的 super 调用更强大。
可以使用 mro() 方法来查看方法解析顺序。
class ClassA:
def foo(self):
print("foo in class A.")
class ClassB:
def foo(self):
print("foo in class B.")
class SubClass(ClassA, ClassB):
def foo(self):
super().foo()
print("foo in sub-class.")
>>> x = SubClass()
>>> x.foo()
foo in class A.
foo in sub-class.
>>> print(SubClass.mro())
[, , , ]
>>> print(issubclass(SubClass, ClassB))
True
类方法与静态方法
@classmethod:把方法封装成类方法。类方法隐含的第一个参数就是类,就像实例方法接收实例作为参数一样。
@staticmethod:将方法转换为静态方法。静态方法不会接收隐式的第一个参数。类似于 Java 和 C++ 中的静态方法。
class MyClass:
name = 'A Demo Class'
@classmethod
def foo(cls, message):
print('foo:', cls.name, message) # 类方法可以通过 cls 访问类变量
@staticmethod
def bar(message):
print('bar:', message) # 静态方法如果要访问类变量,需要显式地使用 MyClass.name
>>> MyClass.foo('hello')
foo: A Demo Class hello
>>> MyClass.bar('hello')
bar: hello
>>> x = MyClass()
>>> x.foo('world')
foo: A Demo Class world
>>> x.bar('world')
bar: world
常用内置函数
完整的内置函数参考:https://docs.python.org/zh-cn/3/library/functions.html
其中包括了 bytes()、list()、range()、str() 等构造函数,另外之前也已经见到过 help()、id()、isinstance()、issubclass()、len()、print()、repr()、reversed()、round()、sorted()、super() 等函数。
下面再列出几个常用的内置函数:
abs()、hex()、divmod()、max()、min()abs(x):返回一个数的绝对值。
hex(x):将整数转换为以“0x”为前缀的小写十六进制字符串。(注意区别于 bytes.hex() 和 bytearray.hex() )
divmod(a,b):返回两个数做整数除法的商和余数。
max(iterable, *[, key, default]):返回可迭代对象中最大的元素。
max(arg1, arg2, *args[, key]):返回两个及以上实参中最大的。
min(iterable, *[, key, default]):返回可迭代对象中最小的元素。
min(arg1, arg2, *args[, key]):返回两个及以上实参中最小的。
getattr(object, name[, default]):返回对象命名属性的值。
hasattr(object, name):如果字符串 name 是对象的属性之一的名称,则返回 True,否则返回 False。
setattr(object, name, value):给对象属性赋值。如 setattr(x, 'foobar', 123) 等价于 x.foobar = 123 。
chr(i):返回整数 i 对应的 Unicode 字符的字符串格式。
ord(c):返回 Unicode 字符 c 的整数码值。
>>> hex(ord('中'))
'0x4e2d'
>>> chr(0x4e2d)
'中'
>>> 'u4e2d'
'中'
zip()
zip(*iterables, strict=False):在多个迭代器上并行迭代,从每个迭代器返回一个数据项组成元组。
>>> for item in zip([1, 2, 3], ['sugar', 'spice', 'everything nice']): ... print(item) ... (1, 'sugar') (2, 'spice') (3, 'everything nice')
>>> for a,b in zip([1, 2, 3], ['sugar', 'spice', 'everything nice']): ... print(a,b) ... 1 sugar 2 spice 3 everything nice
标准库
Python 3 标准库参考:https://docs.python.org/zh-cn/3/library/index.html
除了前面已经讲到的内容,标准库中还提供了许多模块。如:datetime、math、random、hashlib、uuid、json、base64、http、urllib 等等。
PyPI(cheeseshop)
https://github.com/catherinedevlin/python_learners_glossary
PyPI, pronounced “Pie-Pee-Eye” and also known as The Cheeseshop, is the “Python Packaging Index”. It is where you can publish and download open source Python packages.
PyPI 是 Python Package Index 的缩写,以前被叫做 cheeseshop,是 Python 官方的开源包索引,可以理解为 Java 的 Maven 库。使用 pip install 命令就可以安装上面的 python 包。



