文章目录
Python3 面向对象及标准库
面向对象
术语介绍
类(Class):
用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
方法:
类中定义的函数。
类变量:
类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
数据成员:
类变量或者实例变量用于处理类及其实例对象的相关的数据。
方法重写:
如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
局部变量:
定义在方法中的变量,只作用于当前实例的类。
实例变量:
在类的声明中,属性是用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。
继承:
即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。
实例化:
创建一个类的实例,类的具体对象。
对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
注意:python是多继承
类定义:
class ClassName:
<statement-1>
...
<statement-N>
类实例化后可以使用其属性,实际上创建一个类之后,可以通过类名访问其属性
ClassName.name等同于下面两句
className = ClassName()
className.name
类对象:
类对象支持两种操作:属性引用(obj.name)和实例化
class ClassName:
name = 'g'
def getName(self):
return self.name
实例化:
className = ClassName()
访问属性和方法
className.name
className.getName()
构造方法init:
python类对象的构造器是__init__(self),该方法在类实例化时自动调用
其中self代表类的实例,相当于java的this,不同于this,self位置必须处于类方法的第一个参数
注意!
python类继承构造方法__init__()的执行顺序:
若子类定义了自己的init,则子类只会执行自己的init(此时需要super关键字调用父类的构造方法
super(Son, self).__init__(),或使用类名.__init__(self)调用父类的构造方法)
若子类未定义自己的init,则子类会沿着向上的链路找到父类的init方法并执行
多继承init的执行,按照多继承从左向右的优先级查找
即
class E(C, D):
class C(A):
class D(B):
查找顺序为E->C->A->D->B
class E(C, D):
class C(A):
class D(A):
查找顺序为E->C->D->A
类的方法:
使用def定义方法,不同于函数,类的方法必须包含参数self且为第一个参数
静态方法和类方法:
静态方法和类方法是这样创建的:将它们分别包装在staticmethod和classmethod类的对象中。静态方法的定义中没有参数self,可直接通过类来调用。类方法的定义中包含类似于self的参数,通常被命名为cls。
实例:
class MyClass:
def smeth():
print('This is a static method')
smeth = staticmethod(smeth)
def cmeth(cls):
print('This is a class method of', cls)
cmeth = classmethod(cmeth)
可以用装饰器替代以上手工包装替换的步骤:
class MyClass:
@staticmethod
def smeth():
print('This is a static method')
@classmethod
def cmeth(cls):
print('This is a class method of', cls)
定义之后使用方法如下:
MyClass.smeth()
MyClass.cmeth()
继承:
python支持多继承,若基类中有相同的方法名,而在子类未指定则python按从左向右的顺序搜索
class DerivedClass(A, [B, [C, ...]]):
<statement-1>
...
<statement-N>
方法重写:
子类可以重写父类的方法
super()函数用于调用父类的一个方法
super(子类类型,子类实例).method() #用子类对象调用父类已被覆盖的method方法
类属性与方法
类的私有属性:
两个下划线开头,声明属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时self.__private_attrs
类的私有方法:
实例不能直接访问私有属性和私有方法
__private_method:两个下划线声明该方法为私有方法,只能在类的内部调用,self.__private_method
强制访问私有属性和方法:
class A:
__name
def __get():
pass
pass
A._A__name
A._A__get()
通过实例/类名_类型.__私有属性/方法 可以访问私有
若不希望名称被修改,又想发出不要从外部修改属性或方法的信号,可用一个下划线打头
_attr,_method()
这是一种约定,外部可以访问但是 导模块会无视 如from module import * 不会导入以一个下划线打头的名称
类的专有方法:可以重写
__setitem__ : 按照索引赋值
__getitem__: 按照索引获取值
__len__: 获得长度
__cmp__: 比较运算
__call__: 函数调用
__add__: 加运算
__sub__: 减运算
__mul__: 乘运算
__truediv__: 除运算
__mod__: 求余运算
__pow__: 乘方
重写__add__和__str__方法可以对自定义对象进行+ 运算符并能打印
常用专有属性 | 说明 | 触发场景 |
---|---|---|
init | 构造初始化函数 | 创建实例后,赋值时使用,在__new__后 |
new | 生成实例所需属性 | 创建实例时 |
class | 实例所在的类 | 实例.class |
str | 实例字符串表示,可读性 | print(类实例),如没实现,使用repr结果 |
repr | 实例字符串表示,准确性 | 类实例 回车 或者 print(repr(类实例)) |
del | 析构函数,释放对象时使用 | del删除实例 |
dict | 实例自定义属性 | vars(实例.dict) |
doc | 类文档,子类不继承 | help(类或实例) |
getattribute | 属性访问拦截器 | 访问实例属性时 |
bases | 类的所有父类构成元素 | 类名.bases |
__getattr__ , __setattr__等方法:
可以拦截对对象属性的所有访问企图,用途之一是在旧式类中实现特性。要在属性被访问时执行一段代码,需要使用下面4个魔法方法:
- __getattribute__(self, name):在属性被访问时自动调用(只适用于新式类)
- __getattr__(self, name):在属性被访问而对象没有这样的属性时自动调用
- __setattr__(self, name, value):试图给属性赋值时自动调用
- __delattr__(self, name):试图删除属性时自动调用
class Rectangle:
def __init__(self):
self.width = 0
self.height = 0
def __setattr__(self, name, value):
if name == 'size':
self.width, self.height = value
else:
self.__dict__[name] = value
def __getattr__(self, name):
if name == 'size':
return self.width, self.height
else:
raise AttributeError()
__getattribute__的坑:
class Person(object):
def __getattribute__(self,obj):
print("---test---")
if obj.startswith("a"):
return "hahha"
else:
return self.test
def test(self):
print("heihei")
t.Person()
t.a #返回hahha
t.b #会让程序死掉
#原因是:当t.b执行时,会调用Person类中定义的__getattribute__方法,但是在这个方法的执行过程中
#if条件不满足,所以 程序执行else里面的代码,即return self.test 问题就在这,因为return 需要把
#self.test的值返回,那么首先要获取self.test的值,因为self此时就是t这个对象,所以self.test就是
#t.test 此时要获取t这个对象的test属性,那么就会跳转到__getattribute__方法去执行,即此时产
#生了递归调用,由于这个递归过程中 没有判断什么时候推出,所以这个程序会永无休止的运行下去,又因为
#每次调用函数,就需要保存一些数据,那么随着调用的次数越来越多,最终内存吃光,所以程序 崩溃
#
# 注意:以后不要在__getattribute__方法中调用self.xxxx
尽量使用property函数替代以上魔法方法
self和cls:
self指向对象本身
cls表示这个类本身
特性property
通过property函数将存取方法作为参数(获取方法在前,设置方法在后)创建一个特性,然后将属性名称关联到这个特性
calss Rectangle:
def __init__(self):
self.width = 0
self.height = 0
def set_size(self,size):
self.width, self.height = size
def get_size(self):
return self.width, self.height
size = property(get_size, set_size)
r = Rectangle()
r.width = 10
r.height = 5
r.size
=>(10,5)
r.size = 150, 100
r.width
=>150
调用函数property时,可以不指定参数,指定一个参数,指定三个参数或指定四个参数。
若没有指定参数则创建的特性将既不可读也不可写。
若指定了一个参数(获取方法),创建的特性将是只读的
第四个参数也是可选的,指定一个文档字符串
这些参数分别为fget,fset,fdel,doc
解析:
property其实并不是函数,而是一个类。它的实例包含一些魔法方法
这些魔法方法为__get__,__set__,__delete__,它们一道定义了所谓的描述符协议。只要对象实现了这些方法中的任何一个,它就是一个描述符
描述符的独特之处在于其访问方式,如,读取属性(具体说,是在实例中访问类中定义的属性)时,若它关联的是一个实现了__get__的对象,将不会返回这个对象,而是调用方法__get__并将结果返回。
@property成为属性函数,可以对属性赋值时做必要的检查,并保证代码的清晰短小,主要有2个作用
- 将方法转换为只读
- 重新实现一个属性的设置和读取方法,可做边界判定
class Money(object):
def __init__(self):
self.__money = 0
@property
def money(self):
return self.__money
@money.setter
def money(self, value):
if isinstance(value, int):
self.__money = value
else:
print("error:不是整型数字")
探讨继承
确定一个类是否是另一个类的子类
内置方法issubclass
issubclass(Student, People)
=>True
若有一个类,想知道其基类,可访问特殊属性__bases__
Student.__bases__
=>class __main__.People at 0x324e43
确定类属于哪个类
isinstance(s,str)
s.__class__
抽象基类 abc模块
from abc import ABC, abstractmethod
class Talker(ABC):
@abstractmethod
def talk(self):
pass
@为装饰器
抽象类的特征是不能实例化
常用函数:
callable(object) 判断对象是否是可调用的(如是否是函数或方法)
getattr(object, name[, default]) 获取属性的值,还可提供默认值
hasattr(object, name) 确定对象时候有指定的属性
setattr(object, name, value) 将对象的指定属性设置为指定的值
元类
类也是对象,只要使用关键字class,Python解释器在执行的时候就会创建一个对象。
class Obj():
pass
将在内存中创建一个对象,名字是Obj。这个类对象拥有创建实例对象的能力,可以做以下操作:
- 可以将它赋值给一个变量
- 可以拷贝它
- 可以为它增加属性
- 可以将它作为函数参数传递
动态创建类
通过内建函数type可以动态的创建类
type(Obj()) # <class '__main__.Obj'>
type(Obj) # <type 'type'>
type对Obj查看类型时,答案是type 即元类
type可以接受一个类的描述作为参数,然后返回一个类
type(类名, 由父类名称组成的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
class Test: #定义了一个Test类
pass
Test() #创建了一个Test类的实例对象
等同于下面
Test = type("Test",(),{}) #定了一个Test类
Test() #创建了一个Test类的实例对象
help(Test) # help查看Test类结构
可以使用type创建带有属性或继承关系的类:
Animal = type("Animal", (), {"name" : "Animal"})
等效于
class Animal():
name = "Animal"
Dog = type("Dog", (Animal,), {})
class Dog(Animal):
pass
使用type创建带有方法的类:
先定义方法,再将方法赋值给属性
实例方法:
def getName(self):
print(self.name)
Dog = type("Dog", (Animal,), {"getName" : getName})
hasattr(Dog, "getName") # 判断Dog类中是否有getName这个属性 返回True
静态方法:
@staticmethod
def staticTest():
print("static method")
Dog = type("Dog", (Animal,), {"getName" : getName, "staticT" : staticTest})
dog = Dog()
dog.getName()
dog.staticT() # staticT 映射 staticTest
类方法:
@classmethod
def classTest(cls):
print(cls)
Dog = type("Dog", (Animal,), {"classTest" : classTest})
dog = Dog()
dog.classTest()
以上通过type() 动态创建类 实际通过元类实现
class Foo(object):
pass
实际上,解释器将其解释为:Foo = type('Foo', (object,), {})
所谓元类
元类是类的类对象,即类是元类的实例,Python中默认的元类为type,动态自定义元类的方式实现对类的创建
元类就是用来创建类的
MyCls = MetaClass() # 元类创建一个对象,即类对象
MyObj = MyCls() # 类创建实例对象
Python中所有的东西都是对象;包括整数、字符串、函数以及类。且它们都是从一个类创建而来,这个类就是type
以下展示各个对象的__class__属性
字符串:type 'str'
整数:type 'int'
函数:type 'function'
实例对象:class '__main__类名' 连续调用两次__class__ 返回type类型
类:type 'type'
python 3 中,定义 metaclass 的语法:
class Foo(metaclass=Singleton ):
pass
__metaclass__属性
可以在定义一个类的时候为其添加__metaclass__属性
class Foo(object): # py3默认加载object
__metaclass__ = ... # Python2用法,Python3的用法为i.e. the __metaclass__ attribute is no longer used, in favor of a keyword argument in the list of base classes
...
若定义类时指定了__metaclass__属性,则python会用元类创建类Foo;顺序为: class Foo(object)类定义完,不过还没在内存中创建,python会在类的定义中寻找__metaclass__属性,若有则用它创建类Foo,没有就使用内建的type来创建该类。
实际上python做了以下操作:
- Foo中有__metaclass__属性吗?有就通过该属性创建名为Foo的类(对象)
- 若没有__metaclass__,python会继续在父类中寻找,并尝试做第一步同样的操作
- 若python在父类中找不到__metaclass__,则会在模块层次中寻找该属性,并尝试做第一步同样的操作
- 若完全找不到该属性,则通过内置的type创建这个类对象。
__metaclass__中可赋值type或使用到type或子类化type的代码
自定义元类
元类的主要目的是当创建类时能够自动改变类。如,模块里所有类的属性都为大写,则通过模块层次设定__metaclass__,这个模块中的所有类都会通过这个元类来创建
实例:
def upper_attr(future_class_name, future_class_parents, future_class_attr):
#遍历属性字典,把不是__开头的属性名字变为大写
newAttr = {}
for name,value in future_class_attr.items():
if not name.startswith("__"):
newAttr[name.upper()] = value
#调用type来创建一个类
return type(future_class_name, future_class_parents, newAttr)
class Foo(object, metaclass=upper_attr):
bar = 'bip'
print(hasattr(Foo, 'bar'))
print(hasattr(Foo, 'BAR'))
f = Foo()
print(f.BAR)
用class当做元类来使用:
class UpperAttrMetaClass(type):
# __new__ 是在__init__之前被调用的特殊方法
# __new__是用来创建对象并返回之的方法
# 而__init__只是用来将传入的参数初始化给对象
# 你很少用到__new__,除非你希望能够控制对象的创建
# 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__
# 如果你希望的话,你也可以在__init__中做些事情
# 还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用
def __new__(cls, future_class_name, future_class_parents, future_class_attr):
#遍历属性字典,把不是__开头的属性名字变为大写
newAttr = {}
for name,value in future_class_attr.items():
if not name.startswith("__"):
newAttr[name.upper()] = value
# 方法1:通过'type'来做类对象的创建
# return type(future_class_name, future_class_parents, newAttr)
# 方法2:复用type.__new__方法
# 这就是基本的OOP编程,没什么魔法
# return type.__new__(cls, future_class_name, future_class_parents, newAttr)
# 方法3:使用super方法
return super(UpperAttrMetaClass, cls).__new__(cls, future_class_name, future_class_parents, newAttr)
# python3的用法
class Foo(object, metaclass = UpperAttrMetaClass):
bar = 'bip'
print(hasattr(Foo, 'bar'))
# 输出: False
print(hasattr(Foo, 'BAR'))
# 输出:True
元类本身:
- 拦截类的创建
- 修改类
- 返回修改之后的类
标准库概览
操作系统接口
os模块提供了与操作系统相关联的函数
import os # 建议使用import os 而非from os import 如此可以保证os.open()不会覆盖内置函数open()
os.getcwd() # 返回当前工作目录
os.chdir('/server/accesslogs') # 修改当前的工作目录
os.system('mkdir today') # 执行系统命令mkdir
针对日常的文件和目录管理任务,:mod:shutil模块提供了一个易于使用的高级接口
import shutil
shutil.copyfile('data.db', 'archive.db')
shutil.move('/build/executables', 'installdir')
字符串正则匹配
re模块为高级字符串处理提供了正则表达式工具:
import re
re.findall(r'\bf[a-z]*', str) # 从str中匹配正则表达式,返回匹配的列表
re.sub(r'(\b[a-z]+)', str) #
str.replace('too', 'two') # 字符串str替换too为two
数学模块为浮点运算提供了对底层C函数库的访问:
import math
math.cos()
math.log()
random提供了生成随机数的工具
import random
random.choice(seq)
random.randrange(n) # random integer chosen from range(n
random.random() # random float [0,1)
访问互联网
有几个模块用于访问互联网以及处理网络通信协议。最简单的是两个用于处理urls接收的数据的urlib.request以及用于发送电子邮件的smtplib
from urlib.request import urlopen
for line in urlopen('www.baidu.com'):
line = line.decode('utf-8') # Decoding the binary data to text
if 'EST' in line or 'EDT' in line: # look for Eastern Time
print(line)
import smtplib
server = smtplib.SMTP('host')
server.sendmail(tomail, frommail, 'To:', 'From:')
server.quit()
日期和时间
datetime模块为日期和时间处理提供了方法,并支持格式化输出,该模块还支持时区处理
from datetime import date
计算程序运行时间:
1.程序开始到程序结束的运行时间
import datetime
start = datetime.datetime.now()
end = datetime.datetime.now()
print ((end - start).seconds)
2.
start = time.time()
end = time.time()
print (end-start)
3.程序cpu时间
start = time.clock()
end = time.clock()
print (end-start)
数据压缩
以下模块直接支持通用的数据打包和压缩格式:zlib,gzip,zipfile,tarfile
import zlib
性能度量
timeit