class A(B):
pass
a = A()
isinstance(a, B) # 返回True, A是B的子类 a也是B的一种
# OOP和组合: "has- a"的关系
pass
# OOP和委托: "包装"对象 在Python中委托通常是以"__getattr__"钩子方法实现的, 这个方法会拦截对不存在属性的读取
# 包装类(或者称为代理类)可以使用__getattr__把任意读取转发给被包装的对象
class wrapper:
def __init__(self, object):
self.wrapped = object
def __getattr(self, attrname):
print('Trace: ', attrname)
return getattr(self.wrapped, attrname)
# 注:这里使用getattr(X, N)内置函数以变量名字符串N从包装对象X中取出属性 类似于X.__dict__[N]
x = wrapper([1, 2, 3])
x.append(4) # 返回 "Trace: append" [1, 2, 3, 4]
x = wrapper({'a':1, 'b':2})
list(x.keys()) # 返回 "Trace: keys" ['a', 'b']
-- 类的伪私有属性:使用__attr
class C1:
def __init__(self, name):
self.__name = name # 此时类的__name属性为伪私有属性 原理 它会自动变成self._C1__name = name
def __str__(self):
return 'self.name = %s' % self.__name
I = C1('tom')
print(I) # 返回 self.name = tom
I.__name = 'jeey' # 这里无法访问 __name为伪私有属性
I._C1__name = 'jeey' # 这里可以修改成功 self.name = jeey
-- 类方法是对象:无绑定类方法对象 / 绑定实例方法对象
class Spam:
def doit(self, message):
print(message)
def selfless(message)
print(message)
obj = Spam()
x = obj.doit # 类的绑定方法对象 实例 + 函数
x('hello world')
x = Spam.doit # 类的无绑定方法对象 类名 + 函数
x(obj, 'hello world')
x = Spam.selfless # 类的无绑定方法是函数 在3.0之前无效
x('hello world')
-- 获取对象信息: 属性和方法
a = MyObject()
dir(a) # 使用dir函数
hasattr(a, 'x') # 测试是否有x属性或方法 即a.x是否已经存在
setattr(a, 'y', 19) # 设置属性或方法 等同于a.y = 19
getattr(a, 'z', 0) # 获取属性或方法 如果属性不存在 则返回默认值0
#这里有个小技巧,setattr可以设置一个不能访问到的属性,即只能用getattr获取
setattr(a, "can't touch", 100) # 这里的属性名带有空格,不能直接访问
getattr(a, "can't touch", 0) # 但是可以用getattr获取
-- 为类动态绑定属性或方法: MethodType方法
# 一般创建了一个class的实例后, 可以给该实例绑定任何属性和方法, 这就是动态语言的灵活性
class Student(object):
pass
s = Student()
s.name = 'Michael' # 动态给实例绑定一个属性
def set_age(self, age): # 定义一个函数作为实例方法
self.age = age
from types import MethodType
s.set_age = MethodType(set_age, s) # 给实例绑定一个方法 类的其他实例不受此影响
s.set_age(25) # 调用实例方法
Student.set_age = MethodType(set_age, Student) # 为类绑定一个方法 类的所有实例都拥有该方法
"""类的高级话题----类的高级话题----类的高级话题----类的高级话题----类的高级话题----类的高级话题----类的高级话题----类的高级话题----类的高级话题----类的高级话题"""
-- 多重继承: "混合类", 搜索方式"从下到上 从左到右 广度优先"class A(B, C):
pass
-- 类的继承和子类的初始化
# 1.子类定义了__init__方法时,若未显示调用基类__init__方法,python不会帮你调用。
# 2.子类未定义__init__方法时,python会自动帮你调用首个基类的__init__方法,注意是首个。
# 3.子类显示调用基类的初始化函数:
class FooParent(object):
def __init__(self, a):
self.parent = 'I\'m the Parent.'
print('Parent:a=' + str(a))
def bar(self, message):
print(message + ' from Parent')
class FooChild(FooParent):
def __init__(self, a):
FooParent.__init__(self, a)
print('Child:a=' + str(a))
def bar(self, message):
FooParent.bar(self, message)
print(message + ' from Child')
fooChild = FooChild(10)
fooChild.bar('HelloWorld')
-- #实例方法 / 静态方法 / 类方法
class Methods:
def imeth(self, x): print(self, x) # 实例方法:传入的是实例和数据,操作的是实例的属性
def smeth(x): print(x) # 静态方法:只传入数据 不传入实例,操作的是类的属性而不是实例的属性
def cmeth(cls, x): print(cls, x) # 类方法:传入的是类对象和数据
smeth = staticmethod(smeth) # 调用内置函数,也可以使用@staticmethod
cmeth = classmethod(cmeth) # 调用内置函数,也可以使用@classmethod
obj = Methods()
obj.imeth(1) # 实例方法调用 <__main__.Methods object...> 1
Methods.imeth(obj, 2) # <__main__.Methods object...> 2
Methods.smeth(3) # 静态方法调用 3
obj.smeth(4) # 这里可以使用实例进行调用
Methods.cmeth(5) # 类方法调用 <class '__main__.Methods'> 5
obj.cmeth(6) # <class '__main__.Methods'> 6
-- 函数装饰器:是它后边的函数的运行时的声明 由@符号以及后边紧跟的"元函数"(metafunction)组成
@staticmethod
def smeth(x): print(x)
# 等同于:
def smeth(x): print(x)
smeth = staticmethod(smeth)
# 同理
@classmethod
def cmeth(cls, x): print(x)
# 等同于
def cmeth(cls, x): print(x)
cmeth = classmethod(cmeth)
-- 类修饰器:是它后边的类的运行时的声明 由@符号以及后边紧跟的"元函数"(metafunction)组成
def decorator(aClass):.....
@decorator
class C:....
# 等同于:
class C:....
C = decorator(C)
-- 限制class属性: slots属性
class Student:
__slots__ = ('name', 'age') # 限制Student及其实例只能拥有name和age属性
# __slots__属性只对当前类起作用, 对其子类不起作用
# __slots__属性能够节省内存
# __slots__属性可以为列表list,或者元组tuple
-- 类属性高级话题: @property
# 假设定义了一个类:C,该类必须继承自object类,有一私有变量_x
class C(object):
def __init__(self):
self.__x = None
# 第一种使用属性的方法
def getx(self):
return self.__x
def setx(self, value):
self.__x = value
def delx(self):
del self.__x
x = property(getx, setx, delx, '')
# property函数原型为property(fget=None,fset=None,fdel=None,doc=None)
# 使用
c = C()
c.x = 100 # 自动调用setx方法
y = c.x # 自动调用getx方法
del c.x # 自动调用delx方法
# 第二种方法使用属性的方法
@property
def x(self):
return self.__x
@x.setter
def x(self, value):
self.__x = value
@x.deleter
def x(self):
del self.__x
# 使用
c = C()
c.x = 100 # 自动调用setter方法
y = c.x # 自动调用x方法
del c.x # 自动调用deleter方法
-- 定制类: 重写类的方法
# (1)__str__方法、__repr__方法: 定制类的输出字符串
# (2)__iter__方法、next方法: 定制类的可迭代性
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化两个计数器a,b
def __iter__(self):
return self # 实例本身就是迭代对象,故返回自己
def next(self):
self.a, self.b = self.b, self.a + self.b
if self.a > 100000: # 退出循环的条件
raise StopIteration()
return self.a # 返回下一个值
for n in Fib():
print(n) # 使用
# (3)__getitem__方法、__setitem__方法: 定制类的下标操作[] 或者切片操作slice
class Indexer(object):
def __init__(self):
self.data = {}
def __getitem__(self, n): # 定义getitem方法
print('getitem:', n)
return self.data[n]
def __setitem__(self, key, value): # 定义setitem方法
print('setitem:key = {0}, value = {1}'.format(key, value))
self.data[key] = value
test = Indexer()
test[0] = 1; test[3] = '3' # 调用setitem方法
print(test[0]) # 调用getitem方法
# (4)__getattr__方法: 定制类的属性操作
class Student(object):
def __getattr__(self, attr): # 定义当获取类的属性时的返回值
if attr=='age':
return 25 # 当获取age属性时返回25
raise AttributeError('object has no attribute: %s' % attr)
# 注意: 只有当属性不存在时 才会调用该方法 且该方法默认返回None 需要在函数最后引发异常
s = Student()
s.age # s中age属性不存在 故调用__getattr__方法 返回25
# (5)__call__方法: 定制类的'可调用'性
class Student(object):
def __call__(self): # 也可以带参数
print('Calling......')
s = Student()
s() # s变成了可调用的 也可以带参数
callable(s) # 测试s的可调用性 返回True
# (6)__len__方法:求类的长度
def __len__(self):
return len(self.data)
-- 动态创建类type()
# 一般创建类 需要在代码中提前定义
class Hello(object):
def hello(self, name='world'):
print('Hello, %s.' % name)
h = Hello()
h.hello() # Hello, world
type(Hello) # Hello是一个type类型 返回<class 'type'>
type(h) # h是一个Hello类型 返回<class 'Hello'>
# 动态类型语言中 类可以动态创建 type函数可用于创建新类型
def fn(self, name='world'): # 先定义函数
print('Hello, %s.' % name)
Hello = type('Hello', (object,), dict(hello=fn))
# 创建Hello类 type原型: type(name, bases, dict)
h = Hello() # 此时的h和上边的h一致
"""异常相关----异常相关----异常相关----异常相关----异常相关----异常相关----异常相关----异常相关----异常相关----异常相关----异常相关----异常相关----异常相关"""
-- #捕获异常: try:
except: # 捕获所有的异常 等同于except Exception:
except name: # 捕获指定的异常
except name, value: # 捕获指定的异常和额外的数据(实例)
except (name1, name2):
except (name1, name2), value:
except name4 as X:
else: # 如果没有发生异常
finally: # 总会执行的部分
# 引发异常: raise子句(raise IndexError)
raise <instance> # raise instance of a class, raise IndexError()
raise <class> # make and raise instance of a class, raise IndexError
raise # reraise the most recent exception
-- Python3.x中的异常链: raise exception from otherException
except Exception as X:
raise IndexError('Bad') from X
-- assert子句: assert <test>, <data>
assert x < 0, 'x must be negative'
-- with/as环境管理器:作为常见的try/finally用法模式的替代方案
with expression [as variable], expression [as variable]:
# 例子:
with open('test.txt') as myfile:
for line in myfile: print(line)
# 等同于:
myfile = open('test.txt')
try:
for line in myfile: print(line)
finally:
myfile.close()
-- 用户自定义异常: class Bad(Exception):.....
"""
Exception超类 / except基类即可捕获到其所有子类
Exception超类有默认的打印消息和状态 当然也可以定制打印显示:
"""
class MyBad(Exception):
def __str__(self):
return '定制的打印消息'
try:
MyBad()
except MyBad as x:
print(x)
-- 用户定制异常数据
class FormatError(Exception):
def __init__(self, line ,file):
self.line = line
self.file = file
try:
raise FormatError(42, 'test.py')
except FormatError as X:
print('Error at ', X.file, X.line)
# 用户定制异常行为(方法):以记录日志为例
class FormatError(Exception):
logfile = 'formaterror.txt'
def __init__(self, line ,file):
self.line = line
self.file = file
def logger(self):
open(self.logfile, 'a').write('Error at ', self.file, self.line)
try:
raise FormatError(42, 'test.py')
except FormatError as X:
X.logger()
-- 关于sys.exc_info:允许一个异常处理器获取对最近引发的异常的访问
try:
......
except:
# 此时sys.exc_info()返回一个元组(type, value, traceback)
# type:正在处理的异常的异常类型
# value:引发的异常的实例
# traceback:堆栈信息
-- 异常层次
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- ArithmeticError
+-- AssertionError
+-- AttributeError
+-- BufferError
+-- EOFError
+-- ImportError
+-- LookupError
+-- MemoryError
+-- NameError
+-- OSError
+-- ReferenceError
+-- RuntimeError
+-- SyntaxError
+-- SystemError
+-- TypeError
+-- ValueError
+-- Warning
"""Unicode和字节字符串---Unicode和字节字符串----Unicode和字节字符串----Unicode和字节字符串----Unicode和字节字符串----Unicode和字节字符串----Unicode和字节字符串"""
-- Python的字符串类型"""Python2.x"""
# 1.str表示8位文本和二进制数据
# 2.unicode表示宽字符Unicode文本
"""Python3.x"""
# 1.str表示Unicode文本(8位或者更宽)
# 2.bytes表示不可变的二进制数据
# 3.bytearray是一种可变的bytes类型
-- 字符编码方法
"""ASCII""" # 一个字节,只包含英文字符,0到127,共128个字符,利用函数可以进行字符和数字的相互转换
ord('a') # 字符a的ASCII码为97,所以这里返回97
chr(97) # 和上边的过程相反,返回字符'a'
"""Latin-1""" # 一个字节,包含特殊字符,0到255,共256个字符,相当于对ASCII码的扩展
chr(196) # 返回一个特殊字符:Ä
"""Unicode""" # 宽字符,一个字符包含多个字节,一般用于亚洲的字符集,比如中文有好几万字
"""UTF-8""" # 可变字节数,小于128的字符表示为单个字节,128到0X7FF之间的代码转换为两个字节,0X7FF以上的代码转换为3或4个字节
# 注意:可以看出来,ASCII码是Latin-1和UTF-8的一个子集
# 注意:utf-8是unicode的一种实现方式,unicode、gbk、gb2312是编码字符集
-- 查看Python中的字符串编码名称,查看系统的编码
import encodings
help(encoding)
import sys
sys.platform # 'win64'
sys.getdefaultencoding() # 'utf-8'
sys.getdefaultencoding() # 返回当前系统平台的编码类型
sys.getsizeof(object) # 返回object占有的bytes的大小
-- 源文件字符集编码声明: 添加注释来指定想要的编码形式 从而改变默认值 注释必须出现在脚本的第一行或者第二行
"""说明:其实这里只会检查#和coding:utf-8,其余的字符都是为了美观加上的"""
# _*_ coding: utf-8 _*_
# coding = utf-8
-- #编码: 字符串 --> 原始字节 #解码: 原始字节 --> 字符串
-- Python3.x中的字符串应用
s = '...' # 构建一个str对象,不可变对象
b = b'...' # 构建一个bytes对象,不可变对象
s[0], b[0] # 返回('.', 113)
s[1:], b[1:] # 返回('..', b'..')
B = B"""
xxxx
yyyy
"""
# B = b'\nxxxx\nyyyy\n'
# 编码,将str字符串转化为其raw bytes形式:
str.encode(encoding = 'utf-8', errors = 'strict')
bytes(str, encoding)
# 编码例子:
S = 'egg'
S.encode() # b'egg'
bytes(S, encoding = 'ascii') # b'egg'
# 解码,将raw bytes字符串转化为str形式:
bytes.decode(encoding = 'utf-8', errors = 'strict')
str(bytes_or_buffer[, encoding[, errors]])
# 解码例子:
B = b'spam'
B.decode() # 'spam'
str(B) # "b'spam'",不带编码的str调用,结果为打印该bytes对象
str(B, encoding = 'ascii')# 'spam',带编码的str调用,结果为转化该bytes对象
-- Python2.x的编码问题
u = u'汉'
print repr(u) # u'\xba\xba'
s = u.encode('UTF-8')
print repr(s) # '\xc2\xba\xc2\xba'
u2 = s.decode('UTF-8')
print repr(u2) # u'\xba\xba'
# 对unicode进行解码是错误的
s2 = u.decode('UTF-8') # UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
# 同样,对str进行编码也是错误的
u2 = s.encode('UTF-8') # UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 0: ordinal not in range(128)
-- bytes对象
B = b'abc'
B = bytes('abc', 'ascii')
B = bytes([97, 98, 99])
B = 'abc'.encode()
# bytes对象的方法调用基本和str类型一致 但:B[0]返回的是ASCII码值97, 而不是b'a'
-- #文本文件: 根据Unicode编码来解释文件内容,要么是平台的默认编码,要么是指定的编码类型
# 二进制文件:表示字节值的整数的一个序列 open('bin.txt', 'rb')
-- Unicode文件
s = 'A\xc4B\xe8C' # s = 'A?BèC' len(s) = 5
#手动编码
l = s.encode('latin-1') # l = b'A\xc4B\xe8C' len(l) = 5
u = s.encode('utf-8') # u = b'A\xc3\x84B\xc3\xa8C' len(u) = 7
#文件输出编码
open('latindata', 'w', encoding = 'latin-1').write(s)
l = open('latindata', 'rb').read() # l = b'A\xc4B\xe8C' len(l) = 5
open('uft8data', 'w', encoding = 'utf-8').write(s)
u = open('uft8data', 'rb').read() # u = b'A\xc3\x84B\xc3\xa8C' len(u) = 7
#文件输入编码
s = open('latindata', 'r', encoding = 'latin-1').read() # s = 'A?BèC' len(s) = 5
s = open('latindata', 'rb').read().decode('latin-1') # s = 'A?BèC' len(s) = 5
s = open('utf8data', 'r', encoding = 'utf-8').read() # s = 'A?BèC' len(s) = 5
s = open('utf8data', 'rb').read().decode('utf-8') # s = 'A?BèC' len(s) = 5
"""其他----其他----其他----其他----其他----其他----其他----其他----其他----其他----其他----其他----其他----其他----其他----其他----其他----其他----其他"""
-- 60个字符解决FizzBuzz:"""写一个程序, 打印数字1到100, 3的倍数打印“Fizz”来替换这个数, 5的倍数打印“Buzz”, 既是3又是5的倍数的打印“FizzBuzz”"""
for x in range(101):
print("fizz"[x%3*4::]+"buzz"[x%5*4::] or x) # 解释:最主要用到列表(字符串)的子表
-- Python实现任意深度的赋值 例如a[0] = 'value1'; a[1][2] = 'value2'; a[3][4][5] = 'value3'
class MyDict(dict):
def __setitem__(self, key, value): # 该函数不做任何改动 这里只是为了输出
print('setitem:', key, value, self)
super().__setitem__(key, value)
def __getitem__(self, item): # 主要技巧在该函数
print('getitem:', item, self) # 输出信息
# 基本思路: a[1][2]赋值时 需要先取出a[1] 然后给a[1]的[2]赋值
if item not in self: # 如果a[1]不存在 则需要新建一个dict 并使得a[1] = dict
temp = MyDict() # 新建的dict: temp
super().__setitem__(item, temp) # 赋值a[1] = temp
return temp # 返回temp 使得temp[2] = value有效
return super().__getitem__(item) # 如果a[1]存在 则直接返回a[1]
# 例子:
test = MyDict()
test[0] = 'test'
print(test[0])
test[1][2] = 'test1'
print(test[1][2])
test[1][3] = 'test2'
print(test[1][3])
-- Python中的多维数组
lists = [0] * 3 # 扩展list,结果为[0, 0, 0]
lists = [[]] * 3 # 多维数组,结果为[[], [], []],但有问题,往下看
lists[0].append(3) # 期望看到的结果[[3], [], []],实际结果[[3], [3], [3]],原因:list*n操作,是浅拷贝,如何避免?往下看
lists = [[] for i in range(3)] # 多维数组,结果为[[], [], []]
lists[0].append(3) # 结果为[[3], [], []]
lists[1].append(6) # 结果为[[3], [6], []]
lists[2].append(9) # 结果为[[3], [6], [9]]
lists = [[[] for j in range(4)] for i in range(3)]
lists