python类与对象
类与对象
创建类
通过关键字class创建一个Player类,来个栗子
class Player: pass
创建对象
通过类实例化两个对象,来个栗子
class Player: pass p1 = Player() p2 = Player() print(p1) #<__main__.Player object at 0x00000000020E2198> print(p2) #<__main__.Player object at 0x00000000020E2E48>
添加属性
举个栗子
class Player: pass p1 = Player() p1.name = "tom" p1.lives = 3 print(p1.name, p1.lives) #tom 3
面向对象的重点在于不同对象之间的交互
为类添加一些行为,当这些行为触发时,可以改变对象的属性,举个栗子
class Player: def add_one_live(self): self.lives += 1 p = Player() p.lives = 3 p.add_one_live() print(p.lives) #4
参数self
self是对方法所调用对象的引用
类中定义的函数,其第一个形参一般都要显示的传入self,否则在通过类实例调用函数时会报错
class Player: def add_one_live(): self.lives += 1 p = Player() p.add_one_live() #TypeError: add_one_live() takes 0 positional arguments but 1 was given
对象初始化
python类对象的初始化一般包含两个部分:构造函数__new__和对象数据的初始化即初始化方法__init__,
对象的创建是通过构造函数__new__,对象数据的初始化是通过初始化函数__init__,
有的python内置类没有__init__方法,而直接在__new__方法中创建和初始化,如Tuple。
class Player: def __init__(self, lives): self.lives = lives def add_one_live(self): self.lives += 1 p = Player(3) print(p.lives) #3 p.add_one_live() print(p.lives) #4
文档注释
可以在每个类、函数头定义语句的下一行添加字符串
来个栗子
class Player: 'Represents a player in the game' def __init__(self, lives): self.lives = lives def add_one_live(self): """add one live when the player passed one game checkpoint""" self.lives += 1
说明:python -i filename.py,加载这个文件到交互解释器中
help(Player) 查看这个类的格式文档
模块和包
模块
一个python文件就是一个模块
模块的导入
(1)导入整个模块, 在products.py模块需要实例化来自database.py模块中的Database类
import database db = database.Database()
(2)导入模块中的具体类
from database import Database db = Database()
(3)将导入的类重命名
from database import Database as DB db = DB()
(4)一次导入模块中的多个类
from database import Database, Query
(5)禁止使用import *
from database import *
说明:如果使用,将花费更多的时间去找出类的位置,丧失某些编辑器的代码自动补全,跳转到自定义类位置等功能
包
一个包是一个目录下模块的集合,目录的名字就是包的名字,
我们只需在目录下添加一个名为__init__.py的文件就可以告诉python这个目录是一个包
taobao/ main.py ecommerce/ __init__.py database.py products.py contact/ __init__.py email.py payments/ __init__.py square.py stripe.py paypal.py
来个栗子
说明:目录中一共有3个包,在taobao这个目录下有1个ecommerce包,在ecommerce目录下有1个payments包和1个contact包
绝对导入
指定我们想要导入的模块或函数的完整路径,比如我们想要访问products模块中的Product类,可以用以下三种方法进行绝对导入
import ecommerce.products product = ecommerce.products.Product()
or
from ecommercek.products import Product product = Product
or
from ecommerce import products product = products.Product()
说明:这些语句可以在taobao目录下的任何模块中运行
如果products模块下有10个左右的类是我需要使用的,通常使用第三种方法导入
如果products模块下只有2个类是我需要的,通常使用第二种方法导入
相对导入
应用1,寻找与当前模块在位置上有相对关系的类或函数,比如我们想在products模块中导入与之相邻的database模块中的Database类
from .database import Database
点号表示使用当前包内的database模块,即ecommerce包
应用2,如果我们要在paypal模块中使用父包中的database模块,可以用两点号来实现
from ..database import Database
当然,我们也可以利用更多的点来导入更高层级包中的模块,也可以一边后退回到其他较深层
应用3,比如,我们想在paypal模块中使用contact包下的email模块中的send_email函数
from ..contact.email import send_email
说明:..contact.email用了两个点号回到了payments包的上一层,然后用正常的package.module语法回到了contact包那一层
我们也可以从包中导入代码
应用4,比如,database模块中有一个变量db,会有从许多不同的包来访问,import ecommerace.db显然比import ecommerce.database.db更方便
在我们的例子中,如果ecommerce/__init__.py中包含如下一行代码
from .database import db
那么我们可以在main.py和其他任何文件中直接访问ecommerce包中的db属性
from ecommerce import db
__init__.py文件是与其他模块通信的主要节点,但是代码可以在内部组织为几个不同的模块或子包。
组织模块
栗子1,如果我们想要在多个模块中导入Database类并进行实例化,我们可以在多个模块中导入类DataBase并进行实例化,
但是更为合理的做法是只有一个全局的Database实例对象,database.py模块代码如下
class Database: pass database = Database()
分析
但是这存在一个问题,由于模块中调用的代码都会在导入时立即执行,当我们在对database.py模块导入时,Database实例对象就立即被创建,
这通常发生在程序启动时,而数据库的连接又需要时间,这会增加程序启动的时间。
那我们怎么办?当然来推迟Database对象实例的创建时间。
class Database: pass database = None def init_database(): global database database = Database()
栗子2,一个程序的main.py模块,需要从其他程序中的模块中导入某些函数或类,然而一旦导入,所有这些模块层的代码都会立即执行,
但是我们只想访问其中的几个函数。
分析:为了解决这一问题,我们可以将启动的代码放到一个函数中,一般叫main,只有模块作为脚本运行时才执行这一函数。
class Util: pass def main(): util = Util() print(util) pass if __name__ == '__main__': main()
说明:将所有的脚本代码包含在if __name__ == '__main__':下,以防止你写的函数被其他的代码导入。
方法定义在类中,类定义在模块中,模块存在于包中。
说明:这是python一种典型的顺序,类可以定义在任何地方如定义在一个函数的内部,这通常用于一次性对象。
参考资料:《python3面向对象编程》