-
又一个非常方便的Python语法特性
class Product: def __init__(self, value): self.price = value @property def price(self): return self.__price @price.setter def price(self, value): if value < 0: raise ValueError self.__price = value
我们使用@property修饰方法,此时会把方法直接变成同名属性。当我们获取属性的值时,实际上就是调用的此方法。
此时,本身又自动创建了另一个装饰器,负责把一个方法变成属性赋值,即:price的setter方法。于是,修饰的其实是,当我们给price赋值时,实际上就是调用的此方法。此时,price对外表现得就像一个普通的attribute
- 继承
class Animal: def __init__(self): self.age = 1 def eat(self): print('eat') class Mammal(Animal): def walk(self): print('walk') m = Mammal() print(m.age) # 1 m.eat() # eat print(isinstance(m, object)) # True print(issubclass(Mammal, Animal)) # True
所有的类都直接或者间接继承自 object 类
除此以外,还可以非常方便地通过继承扩展内置类型(比如str,list等)
-
继承中的构造器
不同于Java或者C++的构造器机制(类同名,默认构造器,从父到子),Python中的构造器只有一个名字__init__,这也造成了一个神奇的结果:Python的构造器是可以 覆盖 的,而且子类的实例化可以完全不执行父类的构造方法。如果需要在子类的构造器中执行父类的构造器,需要 显式指明 :
def __init__(self): super().__init__() self.weight = 2
另外,如果子类没有自己的构造器,则只会执行父类的构造器,也就是说不存在所谓的 默认构造器 。
-
Python中的抽象类和抽象方法
机制大同小异,不再赘述:
from abc import ABC, abstractmethod class Stream(ABC): # 抽象类 @abstractmethod def read(self): # 抽象方法 pass class MemoryStream(Stream): def read(self): print('read') stream = MemoryStream()
值得注意的是,Python并不支持函数重载,这也意味着所谓的重写(覆盖)的函数签名不包括形参,而只是 函数名 。
-
多态
由于Python中不存在所谓的上转型对象,所以实现多态的方式也有所变化。
可以简单的通过一个函数实现
def operate(control): control.do_something()
显然,只要对象存在该方法,即使不在同一继承体系也可以成功执行,即“is-a“变成了“like-a"
- 纯数据类
from collections import namedtuple Point = namedtuple("Point", ['x', 'y']) p1 = Point(x=1, y=2) p2 = Point(x=1, y=2) print(p1 == p2)
我们不必在重写魔法方法来定义比较规则,同时这种方法创建的数据类是不可变的
- 引入模块的方式
from sales import calc_shipping # 此时calc_shipping就好像在该文件定义一样直接使用 import sales # 此时sales可以看作一个对象,通过sale.doSomething访问模块成员
- Python编译
一旦我在main里import了app并运行,就会产生如图的文件
[站外图片上传中...(image-14bf3c-1553528837227)]
实际上,pycache里存放了app的编译结果作为缓存,加快下次引入app模块的速度。
同时,Python不会编译作为入口的文件(此处是main.py) - 模块路径
就如同Java的类路径,Python也有模块路径用于在程序import时搜寻相应的模块,当前目录就是其中之一。
import sys print(sys.path) # 打印所有模块路径
-
包(package)
包简单来说就是模块的集合,对应到文件层面包就是一个 目录 ,模块就是一个 py文件 。
新建一个目录,并在其中新建一个名为 __init__.py 的文件,这个目录就变成了一个包,同时包还可以有子包,同样创建一个子目录和init文件,以此类推。
那么如何import同级子包中的模块呢?
from 最顶级包.目标包 import 目标模块 from ..目标包 import 目标模块 # 两个点表示上级目录(包)
两种方法分别对应绝对和相对寻址
import app print(dir(app)) #返回scope的所有属性名 print(app.__name__) # 模块名 print(app.__file__) # 路径 print(app.__package__)
值得注意的是,如果直接执行某个模块,模块名(__name__)总会是 __main__ ,所以也就有了常见的:
if __name__ == "__main__": pass