目录
补充:__import__ 和 __init__的理解 如何import非第三方库之外的py文件
定义类(init只负责初始化)
保护对象的属性(getter setter)
__call__()方法
__del__() 方法
单继承
多继承
类属性和实例属性
动态添加和__slots__
类方法
静态方法
工厂模式
__new__方法(只负责创建)
单例模式和只初始化一次
系统第三方模块
自建模块
列表生成式
深Copy和浅Copy
property
迭代器
闭包
装饰器
作用域
生成器
元类
内建属性
__getattribute__的坑
内建函数
functools
常用系统模块
hashlib
正则表达式
第一种:__import__
首先:两个py文件都在同一目录下,我们可以直接import来用,但是在不同文件目录下为什么不能直接import?
In [1]: import sys
In [2]: sys.path
Out[2]:
['',
'/Library/Frameworks/Python.framework/Versions/3.6/bin',
'/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip',
'/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6',
'/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload',
'/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages',
'/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/IPython/extensions',
'/Users/mintou/.ipython']
In [3]:
可以看下sys模块下面的几个路径,除了第一个,其他都是python包目录弱者site-packages(第三方)目录下了,那么第一个就是默认当前目录下查找,如果我们的两个文件在不同文件夹下,如何使用心里有点数了吧。
可以看到,两个py在同一py1下面,但是在各自不同的目录下面,看右边的代码,在数组index=1的位置插入额外的查询路径,这样,__import__动态导包才可以有效。同理,里面有多层目录,一样是在路径里面继续添加
第二种:__init__.py
首先如果一个文件夹可以通过import导入,这个文件夹必须变成包,就需要在文件夹目录下新建一个__init__.py的文件,当你再次import文件的时候,会执行__init__.py里面的代码,而import也生效了
mintoudeMacBook-Pro:py1 mintou$ tree
.
├── Pageges
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-36.pyc
│ │ └── sendMsg.cpython-36.pyc
│ ├── opps
│ │ ├── __init__.py
│ │ ├── __pycache__
│ │ │ ├── __init__.cpython-36.pyc
│ │ │ └── name.cpython-36.pyc
│ │ └── name.py
│ ├── reveiveMsg.py
│ └── sendMsg.py
└── test.py
还是看树形图吧,很清晰的可以看到test.py和Pageges文件夹是在同一层级的,那么test如何访问并使用Pageges里面的py呢?按第二种方法,我们要让Pageges变成包,因此在其目录下新建一个__init__.py,其中的代码如下
from .import sendMsg
from .import opps
第一句很容易理解,当外部import的时候都会执行__init__.py执行import,那么如果内部还有类似opps的文件夹,一样是和之前一样的处理,继续用__init__.py去执行深层次的import
>>type('foo') == str
True
>>type(2.3) in (int,float)
True
既然有了type()来判断类型,为什么还有isinstance()呢?
一个明显的区别是在判断子类。
type()不会认为子类是一种父类类型。 ----> ismenberofclass
isinstance()会认为子类是一种父类类型。 ----> iskindofclass
class Foo(object):
pass
class Bar(Foo):
pass
print type(Foo()) == Foo
print type(Bar()) == Foo
print isinstance(Bar(),Foo)
True
False
True
class 类名:
方法列表
# 定义汽车类
class Car:
def __init__(self, newWheelNum, newColor):
self.wheelNum = newWheelNum
self.color = newColor
def move(self):
print('车在跑,目标:夏威夷')
# 创建对象
BMW = Car(4, 'green')
print('车的颜色为:%s'%BMW.color)
print('车轮子数量为:%d'%BMW.wheelNum)
__init__()
方法,在创建一个对象时默认被调用,不需要手动调用__init__(self)
中,默认有1个参数名字为self,如果在创建对象时传递了2个实参,那么__init__(self)
中出了self作为第一个形参外还需要2个形参,例如__init__(self,x,y)
__init__(self)
中的self参数,不需要开发者传递,python解释器会自动把当前的对象引用传递进去
class Car:
def __init__(self, newWheelNum, newColor):
self.wheelNum = newWheelNum
self.color = newColor
// 这里的 __str__就是OC里面的discription函数,用来打印类的信息,默认是打印地址的
def __str__(self):
msg = "嘿。。。我的颜色是" + self.color + "我有" + int(self.wheelNum) + "个轮胎..."
return msg
def move(self):
print('车在跑,目标:夏威夷')
BMW = Car(4, "白色")
print(BMW)
如果有一个对象,当需要对其进行修改属性时,有2种方法
为了更好的保存属性安全,即不能随意修改,一般的处理方式为
class People(object):
def __init__(self, name):
self.__name = name
def getName(self):
return self.__name
def setName(self, newName):
if len(newName) >= 5:
self.__name = newName
else:
print("error:名字长度需要大于或者等于5")
xiaoming = People("dongGe")
xiaoming.setName("wanger")
print(xiaoming.getName())
xiaoming.setName("lisi")
print(xiaoming.getName())
实例出来的对象直接调用
class MKJ(object):
"""docstring for MKJ"""
def __call__(self):
print("实例对象调用自己方法")
a = MKJ()
a()
__new__()
它是在创建实例的时候被调用(注意此处的"实例",我在这里并没有说"类的实例",因为除了类,还有元类,元类创建实例的时候也会调用这个函数)
__call__()
官方定义:Called when the instance is "called" as a function; if this method is defined,x(arg1, arg2, ...)
is a shorthand forx.__call__(arg1, arg2, ...)
.
它是在“实例被当成函数调用时”被调用。
举个例子,实例如果是"MKJ
",那么,当你写下"MKJ()
"的时候,该实例(即MKJ
)的创建者(注意:此处提到的创建者既有可能是类,也有可能是元类)中的__call__()
被调用。如果这个实例是一个类,那么它的创建者就是一个元类,如果这个实例是一个对象,那么它的创建者就是一个类。
# __new__ 方法创建 类和对象
class SingleTon(object):
__instance = None
def __new__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = super(SingleTon, cls).__new__(cls, *args, **kwargs)
return cls.__instance
class Banaber(SingleTon):
a = 1
# __call__ 方法创建 元类和类
class SingleTon(type):
__intance = None
def __call__(self, *args, **kwargs):
if self.__intance is None:
self.__intance = super().__call__(*args, **kwargs)
return self.__intance
class Foo(metaclass=SingleTon):
pass
创建对象后,python解释器默认调用__init__()
方法;
当删除一个对象时,python解释器也会默认调用一个方法,这个方法为__del__()
方法
OC 里面的dealloc方法
import time
class Animal(object):
# 初始化方法
# 创建完对象后会自动被调用
def __init__(self, name):
print('__init__方法被调用')
self.__name = name
# 析构方法
# 当对象被删除时,会自动被调用
def __del__(self):
print("__del__方法被调用")
print("%s对象马上被干掉了..."%self.__name)
# 创建对象
dog = Animal("哈皮狗")
# 删除对象
del dog
cat = Animal("波斯猫")
cat2 = cat
cat3 = cat
print("---马上 删除cat对象")
del cat
print("---马上 删除cat2对象")
del cat2
print("---马上 删除cat3对象")
del cat3
print("程序2秒钟后结束")
time.sleep(2)
就是OC的引用计数问题,一样的
class Animal(object):
def __init__(self, name='动物', color='白色'):
self.__name = name
self.color = color
def __test(self):
print(self.__name)
print(self.color)
def test(self):
print(self.__name)
print(self.color)
class Dog(Animal):
def dogTest1(self):
#print(self.__name) #不能访问到父类的私有属性
print(self.color)
def dogTest2(self):
#self.__test() #不能访问父类中的私有方法
self.test()
A = Animal()
#print(A.__name) #程序出现异常,不能访问私有属性
print(A.color)
#A.__test() #程序出现异常,不能访问私有方法
A.test()
print("------分割线-----")
D = Dog(name = "小花狗", color = "黄色")
D.dogTest1()
D.dogTest2()
#coding=utf-8
class base(object):
def test(self):
print('----base test----')
class A(base):
def test(self):
print('----A test----')
# 定义一个父类
class B(base):
def test(self):
print('----B test----')
# 定义一个子类,继承自A、B
class C(A,B):
pass
obj_C = C()
obj_C.test()
print(C.__mro__) #可以查看C类的对象搜索方法时的先后顺序
# ----A test----
# (, , , , )
这里如果继承的方法都是一样的,可以子类优先,类.__mro__会打印出对象搜索的方法
#coding=utf-8
class Cat(object):
def __init__(self,name):
self.name = name
self.color = 'yellow'
class Bosi(Cat):
def __init__(self,name):
# 调用父类的__init__方法1(python2)
#Cat.__init__(self,name)
# 调用父类的__init__方法2
#super(Bosi,self).__init__(name)
# 调用父类的__init__方法3
super().__init__(name)
def getName(self):
return self.name
bosi = Bosi('xiaohua')
print(bosi.name)
print(bosi.color)
class People(object):
name = 'Tom' #公有的类属性
__age = 12 #私有的类属性
p = People()
print(p.name) #正确
print(People.name) #正确
print(p.__age) #错误,不能在类外通过实例对象访问私有的类属性
print(People.__age) #错误,不能在类外通过类对象访问私有的类属性
class People(object):
address = '山东' #类属性
def __init__(self):
self.name = 'xiaowang' #实例属性
self.age = 20 #实例属性
p = People()
p.age =12 #实例属性
print(p.address) #正确
print(p.name) #正确
print(p.age) #正确
print(People.address) #正确
print(People.name) #错误
print(People.age) #错误
class People(object):
country = 'china' #类属性
print(People.country)
p = People()
print(p.country)
p.country = 'japan'
print(p.country) #实例属性会屏蔽掉同名的类属性
print(People.country)
del p.country #删除实例属性
print(p.country)
类属性
,必须通过类对象
去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性
,这种方式修改的是实例属性
,不会影响到类属性
,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性
,除非删除了该实例属性
。Python和其他动态语言一样,可以动态添加方法和属性,这一点很好理解验证,如果不让类能动态添加,就需要__slots属性了
class Person(object):
__slots__ = ("name")
def __init__(self):
self.name = "aaa"
p = Person()
p.age = 11
print(p.name)
# AttributeError: 'Person' object has no attribute 'age'
是类对象所拥有的方法,需要用修饰器@classmethod
来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls
作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以'cls'作为第一个参数的名字,就最好用'cls'了),能够通过实例对象和类对象去访问。
class People(object):
country = 'china'
#类方法,用classmethod来进行修饰
@classmethod
def getCountry(cls):
return cls.country
p = People()
print p.getCountry() #可以用过实例对象引用
print People.getCountry() #可以通过类对象引用
class People(object):
country = 'china'
@staticmethod
#静态方法
def getCountry():
return People.country
print People.getCountry()
从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象cls,那么通过cls引用的必定是类对象的属性和方法;而实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类对象来引用
在父类留下接口,在子类进行实现,因此各个子类表现出来的形式就不同
# 定义一个基本的4S店类
class CarStore(object):
#仅仅是定义了有这个方法,并没有实现,具体功能,这个需要在子类中实现
def createCar(self, typeName):
pass
def order(self, typeName):
# 让工厂根据类型,生产一辆汽车
self.car = self.createCar(typeName)
self.car.move()
self.car.stop()
# 定义一个北京现代4S店类
class XiandaiCarStore(CarStore):
def createCar(self, typeName):
self.carFactory = CarFactory()
return self.carFactory.createCar(typeName)
# 定义伊兰特车类
class YilanteCar(object):
# 定义车的方法
def move(self):
print("---车在移动---")
def stop(self):
print("---停车---")
# 定义索纳塔车类
class SuonataCar(object):
# 定义车的方法
def move(self):
print("---车在移动---")
def stop(self):
print("---停车---")
# 定义一个生产汽车的工厂,让其根据具体得订单生产车
class CarFactory(object):
def createCar(self,typeName):
self.typeName = typeName
if self.typeName == "伊兰特":
self.car = YilanteCar()
elif self.typeName == "索纳塔":
self.car = SuonataCar()
return self.car
suonata = XiandaiCarStore()
suonata.order("索纳塔")
class A(object):
def __init__(self):
print("这是 init 方法")
def __new__(cls):
print("这是 new 方法")
return object.__new__(cls)
A()
__new__
至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供
__new__
必须要有返回值,返回实例化出来的实例,这点在自己实现__new__
时要特别注意,可以return父类__new__
出来的实例,或者直接是object的__new__
出来的实例
__init__
有一个参数self,就是这个__new__
返回的实例,__init__
在__new__
的基础上可以完成一些其它初始化的动作,__init__
不需要返回值
我们可以将类比作制造商,__new__
方法就是前期的原材料购买环节,__init__
方法就是在有原材料的基础上,加工,初始化商品环节
class Dog(object):
"""docstring for Dog"""
# def __init__(self, arg):
# super(Dog, self).__init__()
# self.arg = arg
#
instance = None
firstInit = False
def __new__(cls,name,age):
if not cls.instance:
cls.instance = object.__new__(cls)
return cls.instance
else:
return cls.instance
def __init__(self,name,age):
if Dog.firstInit == False:
self.name = name
self.age = age
Dog.firstInit = True
dog1 = Dog("Tom",12)
print("%s + %s"%(id(dog1),dog1.name))
dog2 = Dog("Jerry",20)
print("%s + %s"%(id(dog2),dog2.name))
4328148104 + Tom
4328148104 + Tom
***Repl Closed***
如果装的是3.6,系统自带的是2.7
In [1]: import sys
In [2]: import os
In [3]: os.__file__
Out[3]: '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/os.py'
cd到路径下面就是系统默认自带的模块
安装第三方模块
sudo pip3 install pygame 在版本3路径下安装
sudo pip install pygame 在版本2路径下安装
两种检验方式是否安装成功
1.ipython3环境下
import 安装的模块,如果不报错,即可
2.在模块目录下查找
import math
#这样会报错
print sqrt(2)
#这样才能正确输出结果
print math.sqrt(2)
from 模块名 import 函数名1,函数名2....
通过这种方式引入的时候,调用函数时只能给出函数名,不能给出模块名,但是当两个模块中含有相同名称函数的时候,后面一次引入会覆盖前一次引入。也就是说假如模块A中有函数function( ),在模块B中也有函数function( ),如果引入A中的function在先、B中的function在后,那么当调用function函数的时候,是去执行模块B中的function函数。
如果想一次性引入math中所有的东西,还可以通过from math import *来实现
Python的from语句让你从模块中导入一个指定的部分到当前命名空间中
语法如下:
from modname import name1[, name2[, ... nameN]]
例如,要导入模块fib的fibonacci函数,使用如下语句:
from fib import fibonacci
自建py模块的时候在其他文件import的时候,是会把模块的文件都执行一遍,例如你里面有print这些函数,也会在import的时候直接导入,因此在自建模块的里面,要用__name__来控制,自己运行__name__ = __main__ 如果是import运行,__name__就是test,因此可以根据这个字段判断是否在自测模块的时候打印,别人import的时候不执行打印
from xxx import *该方法导入的模块,如果模块里面有__all__参数,只能导入__all__ = ["函数1","类1"]列出的功能函数或者方法类
给程序传参
import sys
print(sys.argv)
在模块里面导入sys,然后终端执行
python3 xxx.py 参数一 参数二
模块里面就会接收到参数打印数组出来
Range在Python2中你要多少数组大小是直接给你的,因此你这样拿的话内存会可能爆炸
在Python3中就是动态给你的,而不是你Range多少都直接返回数组给你,你用到的时候才会给你数据分配空间
In [2]: a = [i for i in range(1,20)]
In [3]: a
Out[3]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
In [4]: a = [i for i in range(1,20) if i%2==0]
In [5]: a
Out[5]: [2, 4, 6, 8, 10, 12, 14, 16, 18]
In [6]: a = [(i,j) for i in range(1,3) for j in range(1,5)]
In [7]: a
Out[7]: [(1, 1), (1, 2), (1, 3), (1, 4), (2, 1), (2, 2), (2, 3), (2, 4)]
deepcopy(递归深copy)
In [71]: a = [1,2,3]
In [72]: b = [4,5,6]
In [73]: c = [a,b]
In [74]: c
Out[74]: [[1, 2, 3], [4, 5, 6]]
In [75]: id(a)
Out[75]: 4406509384
In [76]: id(b)
Out[76]: 4404327368
In [77]: id(c[0])
Out[77]: 4406509384
In [78]: import copy
In [79]: d = copy.deepcopy(c)
In [80]: id(c)
Out[80]: 4404619720
In [81]: id(d)
Out[81]: 4407108424
In [82]: id(d[0])
Out[82]: 4404570248
In [83]: a.append(1000)
In [84]: a
Out[84]: [1, 2, 3, 1000]
In [85]: c[0]
Out[85]: [1, 2, 3, 1000]
In [86]: d[0]
Out[86]: [1, 2, 3]
In [87]: d
Out[87]: [[1, 2, 3], [4, 5, 6]]
上面的是递归深copy的操作,因此复制出来的是完全开辟另一个空间的全部内容复制,源对象的修改不会影响后面
copy(深copy)-->可变类型数组
In [89]: a = [1,2,3]
In [90]: b = [4,5,6]
In [91]: c = [a,b]
In [92]: c
Out[92]: [[1, 2, 3], [4, 5, 6]]
In [93]: d = copy.copy(c)
In [94]: a
Out[94]: [1, 2, 3]
In [95]: id(a)
Out[95]: 4407079304
In [96]: id(b)
Out[96]: 4407079816
In [97]: id(c)
Out[97]: 4404548808
In [98]: id(d)
Out[98]: 4407106120
In [99]: id(d[0])
Out[99]: 4407079304
In [100]: a.append(1000)
In [101]: a
Out[101]: [1, 2, 3, 1000]
In [102]: b
Out[102]: [4, 5, 6]
In [103]: c
Out[103]: [[1, 2, 3, 1000], [4, 5, 6]]
In [104]: d
Out[104]: [[1, 2, 3, 1000], [4, 5, 6]]
上面的内容可以看出,针对copy的操作,不会进行递归,只是对容器的copy,容器由于是可变类型数组,那么容器的地址c和d是不同的,但是容器内部的指针是不会进行深copy的,沿用之前的对象,源对象改变,能改变后面所有值
copy(深copy)-->不可变元祖
In [111]: a = [1,2,3]
In [112]: b = [4,5,6]
In [113]: c = (a,b)
In [114]: d = copy.copy(c)
In [115]: id(c)
Out[115]: 4406812232
In [116]: id(d)
Out[116]: 4406812232
In [117]: a.append(1000)
In [118]: a
Out[118]: [1, 2, 3, 1000]
In [119]: c
Out[119]: ([1, 2, 3, 1000], [4, 5, 6])
In [120]: d
Out[120]: ([1, 2, 3, 1000], [4, 5, 6])
针对不可变类型的copy就是浅copy,指针copy,其他和上面一样
OC里面的mutableCopy就是python里面的copy
NSMutableArray *a = [[NSMutableArray alloc] initWithArray:@[@"1",@"2",@"3"]];
NSMutableArray *b = [[NSMutableArray alloc] initWithArray:@[@"222",@"333",@"444"]];
NSArray *c = @[a,b];
NSLog(@"%@ %p",c,c);
NSLog(@"a的地址%p",a);
NSLog(@"b的地址%p",b);
NSArray *d = c.mutableCopy;
NSLog(@"%@ %p",d,d);
NSLog(@"a的地址%p",d[0]);
NSLog(@"b的地址%p",d[1]);
NSArray *e = c.copy;
NSLog(@"%@ %p",e,e);
NSLog(@"a的地址%p",e[0]);
NSLog(@"b的地址%p",e[1]);
[a addObject:@"10000"];
NSLog(@"%@",c);
NSLog(@"%@",d);
NSLog(@"%@",e);
2018-06-19 10:24:28.929903+0800 Mutable[3954:70431] (
(
1,
2,
3
),
(
222,
333,
444
)
) 0x600000026a40
2018-06-19 10:24:28.930026+0800 Mutable[3954:70431] a的地址0x6000000489d0
2018-06-19 10:24:28.930111+0800 Mutable[3954:70431] b的地址0x600000048340
2018-06-19 10:24:28.930215+0800 Mutable[3954:70431] (
(
1,
2,
3
),
(
222,
333,
444
)
) 0x604000057af0
2018-06-19 10:24:28.930301+0800 Mutable[3954:70431] a的地址0x6000000489d0
2018-06-19 10:24:28.930390+0800 Mutable[3954:70431] b的地址0x600000048340
2018-06-19 10:24:28.930493+0800 Mutable[3954:70431] (
(
1,
2,
3
),
(
222,
333,
444
)
) 0x600000026a40
2018-06-19 10:24:28.930573+0800 Mutable[3954:70431] a的地址0x6000000489d0
2018-06-19 10:24:28.930670+0800 Mutable[3954:70431] b的地址0x600000048340
2018-06-19 10:24:28.930770+0800 Mutable[3954:70431] (
(
1,
2,
3,
10000
),
(
222,
333,
444
)
)
2018-06-19 10:24:28.930876+0800 Mutable[3954:70431] (
(
1,
2,
3,
10000
),
(
222,
333,
444
)
)
2018-06-19 10:24:28.930994+0800 Mutable[3954:70431] (
(
1,
2,
3,
10000
),
(
222,
333,
444
)
)
下面的图是OC对象的copy操作,基本上操作和python一致
方案一
class Person(object):
__slots__ = ["__age"]
def __init__(self):
super(Person, self).__init__()
self.__age = 100
def getAge(self):
return self.__age
def setAge(self,newAge):
self.__age = newAge
age = property(getAge,setAge)
person = Person()
print(person.age)
person.age = 200
print(person.age)
方案二
class Money(object):
__slots__ = ["__num"]
def __init__(self):
super(Money, self).__init__()
self.__num = 100000
@property
def num(self):
return self.__num
@num.setter
def num(self,newNum):
self.__num = newNum
money = Money()
print(money.num)
money.num = 100000000
print(money.num)
迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
以直接作用于 for 循环的数据类型有以下几种:
一类是集合数据类型,如 list 、 tuple 、 dict 、 set 、 str 等;
一类是 generator ,包括生成器和带 yield 的generator function。
这些可以直接作用于 for 循环的对象统称为可迭代对象: Iterable 。
是否可迭代
In [1]: from _collections_abc import Iterable
In [2]: isinstance(100,Iterable)
Out[2]: False
In [3]: isinstance("dsds",Iterable)
Out[3]: True
In [4]: isinstance({},Iterable)
Out[4]: True
In [5]: isinstance([1,2,3],Iterable)
Out[5]: True
In [6]: isinstance((x for x in range(10)),Iterable)
Out[6]: True
是否迭代器
In [1]: from _collections_abc import Iterator
In [2]: a = [1,2,3]
In [3]: isinstance(a,Iterator)
Out[3]: False
In [4]: isinstance({},Iterator)
Out[4]: False
In [5]: isinstance(x for x in range(10),Iterator)
File "", line 1
isinstance(x for x in range(10),Iterator)
^
SyntaxError: Generator expression must be parenthesized if not sole argument
In [6]: isinstance((x for x in range(10)),Iterator)
Out[6]: True
字符串,list,字典都是可迭代
可迭代的东西不一定是可迭代对象,但是生成器必定是 (x for x in range(10))
可以用iter()来把 数组字典这些可迭代的换成可迭代的对象
这么做有个好处,数组字典一开始都已经开辟了控件,如果你需要用的时候再开辟,就可以转成迭代器来提高性能
- 凡是可作用于 for 循环的对象都是 Iterable 类型;
- 凡是可作用于 next() 函数的对象都是 Iterator 类型
- 集合数据类型如 list 、 dict 、 str 等是 Iterable 但不是 Iterator ,不过可以通过 iter() 函数获得一个 Iterator 对象。
内部函数对外部函数作用域里变量的引用(非全局变量),则称内部函数为闭包。
def test1(number1):
def test2(number2):
print(number1 + number2)
return test2
a = test1(100)
a(200)
a(1000)
a(1)
300
1100
101
***Repl Closed***
按照iOS的的思路去理解,其实关于对象的内存问题,基本都是一样的,有变量强引用的情况下就不会被回收,那么就很好理解闭包了
在函数内部再定义一个函数,并且这个函数用到了外部函数的变量,那么将这个函数和用到一些变量称之为闭包。
可以看到定一个一个test1函数,开辟了内存,有个变量number1,然后函数内部又定义了一个test2函数,由test1函数返回给外部变量a绑定指着,那么外部每次调用a,也就是调用了test1函数内部的test2函数,每次调用都会去拿最早之前传进去的numer1的值,为什么调用完函数之后,test1没有被回收,因为其内部还有个函数,被外部的变量a强引用着,因此只要a不会销毁,test1就会一直存在,number1也就一直存在,好处是调用的时候直接拿,坏处是一直占用着内存
1.闭包似优化了变量,原来需要类对象完成的工作,闭包也可以完成
2.由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存
# 语法糖
def updateTaskWithAuth(func):
def authorlizations():
print("验证权限")
func()
return authorlizations
@updateTaskWithAuth
def func2():
print("业务二")
@updateTaskWithAuth
def func1():
print("业务一")
# 原始调用 一
# func1()
# func2()
# 新增鉴权 二
# func1 = updateTaskWithAuth(func1)
# func1()
# func2 = updateTaskWithAuth(func2)
# func2()
# 依旧原始调用
# 在闭包中,如果不改变原始调用,新增功能,可以另外写一个方法,然后通过传入原先的业务功能例如func1,最终改变func1默认函数指针的指向
# 改为指向闭包内部返回的函数即可,如果用语法糖就需要在业务函数外部加上@闭包函数名,然后原样调用,就是直接赋值的效果
func1()
func2()
其实可以看到,无非就是在调用原函数的时候,在外部不变的情况下,类似iOS的中的RuntimeHook函数,只是实现稍微有点不同而已,Python是通过@语法糖里面的闭包来实现,iOS是Runtime底层交换方法来实现,再不改原先逻辑的情况下,在方法之前嵌入自己的逻辑,例如日志,统计,预处理,清理,校验等场景
这里明白@updateTaskWithAuth就已经会执行一段代码例如
func1 = updateTaskWithAuth(func1)改变了原先func1的指向罢了,但是这句代码是会执行的,当从上到下遇到@装饰时时会先执行的
类似例如DJango中的request请求参数传递
def updateTaskWithAuth(func):
def authorlizations(para,*args, **kwargs):
print('%s--%s-%s'%(para,args,kwargs))
# print("验证权限%s"%args)
func(para)
return authorlizations
@updateTaskWithAuth
def func2(para):
print("业务二")
@updateTaskWithAuth
def func1():
print('业务二')
func2({"a":111}, '参数二', a=1)
{'a': 111}--('参数二',)-{'a': 1}
业务二
调用函数的所有参数都会被装饰器带走传递给闭包函数,最终通过func回调
多个装饰器
def w1(func):
print("装饰1")
def inner():
print("插入功能1")
func()
return inner
def w2(func):
print("装饰2")
def inner():
print("插入功能2")
func()
return inner
@w1
@w2
def func1():
print("业务1")
func1()
装饰2
装饰1
插入功能1
插入功能2
业务1
***Repl Closed***
根据图片分析下,首先编译器遇到@w1和@w2,这里是会有代码执行的,上面有介绍,就不展开了,但是前提是装饰器的下一句代码是方法函数,才会装饰,因此先跳过@w1,然后@w2就会对func函数进行装饰,因此先执行装饰2,然后返回的值就是inner函数,再执行装饰1,执行的时候就是先执行装饰1里面的inner函数,然后在执行装饰2里面的inner函数,好比一个东西,包装的时候由内到外,执行的时候由外到内,这就是多层装饰的逻辑
万能通用装饰器(iOS中的MethodSwizzle效果)
def func(funcTemp):
print("装饰函数----开始装饰")
def innderFunc(*args,**kwargs):
print("装饰函数----执行装饰实际代码例如插入,验证,统计等。。。。。。")
return funcTemp(*args,**kwargs)
return innderFunc
@func
def func1():
print("无参数,无返回值")
@func
def func2():
print("无参数,有返回值")
return "宓珂璟"
@func
def func3(a):
print("有一个参数 %d"%(a))
@func
def func4(a,b,c):
print("有多个参数 %d,%d,%d"%(a,b,c))
return a + b + c
print(func1())
print(func2())
print(func3(100))
print(func4(100,200,300))
装饰函数----开始装饰
装饰函数----开始装饰
装饰函数----开始装饰
装饰函数----开始装饰
装饰函数----执行装饰实际代码例如插入,验证,统计等。。。。。。
无参数,无返回值
None
装饰函数----执行装饰实际代码例如插入,验证,统计等。。。。。。
无参数,有返回值
宓珂璟
装饰函数----执行装饰实际代码例如插入,验证,统计等。。。。。。
有一个参数 100
None
装饰函数----执行装饰实际代码例如插入,验证,统计等。。。。。。
有多个参数 100,200,300
600
***Repl Closed***
这就是一个通用装饰器
如果装饰器要带参数,外面再包一层即可
from time import ctime, sleep
def timefun_arg(pre="hello"):
def timefun(func):
def wrappedfunc():
print("%s called at %s %s"%(func.__name__, ctime(), pre))
return func()
return wrappedfunc
return timefun
@timefun_arg("hehe")
def foo():
print("I am foo")
@timefun_arg("python")
def too():
print("I am too")
foo()
sleep(2)
foo()
too()
sleep(2)
too()
foo()==timefun_arg("hehe")(foo)()
类装饰器
class Test(object):
def __init__(self, func):
print("---初始化---")
print("func name is %s"%func.__name__)
self.__func = func
def __call__(self):
print("---装饰器中的功能---")
self.__func()
#说明:
#1. 当用Test来装作装饰器对test函数进行装饰的时候,首先会创建Test的实例对象
# 并且会把test这个函数名当做参数传递到__init__方法中
# 即在__init__方法中的func变量指向了test函数体
#
#2. test函数相当于指向了用Test创建出来的实例对象
#
#3. 当在使用test()进行调用时,就相当于让这个对象(),因此会调用这个对象的__call__方法
#
#4. 为了能够在__call__方法中调用原来test指向的函数体,所以在__init__方法中就需要一个实例属性来保存这个函数体的引用
# 所以才有了self.__func = func这句代码,从而在调用__call__方法中能够调用到test之前的函数体
@Test
def test():
print("----test---")
test()
globals() 打印出所有全局变量
locals() 打印出函数局部变量里面的所有局部变量
dir(__builtin__) 查看内建模块函数
Python 使用 LEGB 的顺序来查找一个符号对应的对象
locals -> enclosing function -> globals -> builtins
builtins,内建模块的命名空间。
在Python中,这种一边循环一边计算的机制,称为生成器:generator
方法一 [] ---> ()
In [15]: L = [ x*2 for x in range(5)]
In [16]: L
Out[16]: [0, 2, 4, 6, 8]
In [17]: G = ( x*2 for x in range(5))
In [18]: G
Out[18]: at 0x7f626c132db0>
In [19]:
方法二(yield)
def fib(times):
a,b = 0,1
n = 0
print("----1-----")
while n
在循环过程中就会不断调用yield,并且不断中断,只有当我们调用next的时候才会继续执行,遇到yield继续中断,或者直接遍历生成器也能不断调用执行yield
send可以给yield赋值的变量传值
def test():
i = 0
while i < 6:
temp = yield i
print(temp)
i += 1
t = test()
print(next(t))
print(next(t))
t.send("宓珂璟")
print(next(t))
print(next(t))
print(next(t))
# 0
# None
# 1
# 宓珂璟
# None
# 3
# None
# 4
# None
# 5
# ***Repl Closed***
生成器的特点:
对比看下简单的迭代器和生成器,就是把原先数组需要开辟的空间,通过一边使用一边开辟,节约内存
In [2]: def test():
...: n = 0
...: while(n<6):
...: yield n
...: n += 1
...:
In [3]: a = test()
In [4]: a
Out[4]:
In [5]: b = [1,2,3]
In [6]: b
Out[6]: [1, 2, 3]
In [7]: b = iter(b)
In [8]: b
Out[8]:
In [9]: c = (x for x in range(10))
In [10]: c
Out[10]: at 0x106894fc0>
元类就是用来创建类的“东西”
和OC一样,class也是一种对象,他的父类就是元类
>>> print type(1) #数值的类型
>>> print type("1") #字符串的类型
>>> print type(ObjectCreator()) #实例对象的类型
>>> print type(ObjectCreator) #类的类型
类的type是 'type'
In [16]: class MK(object):
...: def __init__(self):
...: self.name = None
...:
In [17]: type(MK)
Out[17]: type
In [18]: a = MK()
In [19]: a
Out[19]: <__main__.MK at 0x1069d5eb8>
In [20]: type(a)
Out[20]: __main__.MK
In [21]: type(MK)
Out[21]: type
In [22]: clear
In [23]: myDogClass = type("MyDog",(),{})
In [24]: myDogClass
Out[24]: __main__.MyDog
In [25]: MK()
Out[25]: <__main__.MK at 0x106971518>
In [27]: print(MK)
In [28]: print(myDogClass)
In [29]: print(myDogClass())
<__main__.MyDog object at 0x106971860>
type(类名, 由父类名称组成的元组(针对继承的情况,可以为空),包含属性的字典(名称和值)以及静态或者类方法)
元类就是类的类
MyClass = MetaClass() #使用元类创建出一个对象,这个对象称为“类”
MyObject = MyClass() #使用“类”来创建出实例对象
你也可以看到如下代码
MyClass = type('MyClass', (), {})
这是因为函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类
Python中所有的东西,注意,我是指所有的东西——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类创建而来,这个类就是type
type是Python里面的内建元类,不过你也可以自己创建
class upperSuperClass(type):
def __new__(cls, className, upperSuperClassName, classAttr):
print('21321312')
newDict = {}
for key, value in classAttr.items():
if not key.startswith("__"):
newDict[key.upper()] = value
return super(upperSuperClass, cls).__new__(cls, className, upperSuperClassName, newDict)
class Foo(object, metaclass=upperSuperClass):
bar = "pip"
f = Foo()
print(f.BAR))
Python做了如下的操作:
元类的作用
In [2]: class Person(object):
...: pass
...:
...:
In [3]: dir(Person)
Out[3]:
['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__']
常用专有属性 | 说明 | 触发方式 |
---|---|---|
__init__ |
构造初始化函数 | 创建实例后,赋值时使用,在__new__ 后 |
__new__ |
生成实例所需属性 | 创建实例时 |
__class__ |
实例所在的类 | 实例.__class__ |
__str__ |
实例字符串表示,可读性 | print(类实例),如没实现,使用repr结果 |
__repr__ |
实例字符串表示,准确性 | 类实例 回车 或者 print(repr(类实例)) |
__del__ |
析构 | del删除实例 |
__dict__ |
实例自定义属性 | vars(实例.__dict__) |
__doc__ |
类文档,子类不继承 | help(类或实例) |
__getattribute__ |
属性访问拦截器 | 访问实例属性时 |
__bases__ |
类的所有父类构成元素 | 类名.__bases__ |
class Itcast(object):
def __init__(self,subject1):
self.subject1 = subject1
self.subject2 = 'cpp'
#属性访问时拦截器,打log
def __getattribute__(self,obj):
if obj == 'subject1':
print('log subject1')
return 'redirect python'
else: #测试时注释掉这2行,将找不到subject2
return object.__getattribute__(self,obj)
def show(self):
print('this is Itcast')
s = Itcast("python")
print(s.subject1)
print(s.subject2)
__getattribute__
例子中后面跟的参数self是自身,obj就是对应的属性字符串名字,属性的访问是会先根据对象的__dict__,然后在类的__dict__中查找,如果没有才会进入上面的__getattribute__方法里面
log subject1
redirect python
cpp
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
__getattribute__ 是属性拦截方法,如果重写,就不会按照先找对象的__dict__,再找类的,__getattr__则是之前的方法找不到才会最终调用的方法。一般不要重写__getattribute__,避免修改处上面的Bug,直接重写__getattr__就好
map函数
map函数会根据提供的函数对指定序列做映射
map(...)
map(function, sequence[, sequence, ...]) -> list
参数序列中的每一个元素分别调用function函数,返回包含每次function函数返回值的list。
#函数需要一个参数
map(lambda x: x*x, [1, 2, 3])
#结果为:[1, 4, 9]
#函数需要两个参数
map(lambda x, y: x+y, [1, 2, 3], [4, 5, 6])
#结果为:[5, 7, 9]
def f1( x, y ):
return (x,y)
l1 = [ 0, 1, 2, 3, 4, 5, 6 ]
l2 = [ 'Sun', 'M', 'T', 'W', 'T', 'F', 'S' ]
l3 = map( f1, l1, l2 )
print(list(l3))
#结果为:[(0, 'Sun'), (1, 'M'), (2, 'T'), (3, 'W'), (4, 'T'), (5, 'F'), (6, 'S')]
filter函数
filter函数会对指定序列执行过滤操作
filter(...)
filter(function or None, sequence) -> list, tuple, or string
Return those items of sequence for which function(item) is true. If
function is None, return the items that are true. If sequence is a tuple
or string, return the same type, else return a list.
filter函数会对序列参数sequence中的每个元素调用function函数,最后返回的结果包含调用结果为True的元素。
返回值的类型和参数sequence的类型相同
filter(lambda x: x%2, [1, 2, 3, 4])
[1, 3]
filter(None, "she")
'she'
reduce函数
In [10]: from functools import reduce
In [11]: reduce(lambda x, y: x+y, ['aa', 'bb', 'cc'], 'dd')
Out[11]: 'ddaabbcc'
In [12]: reduce(lambda x, y: x+y, [1,2,3,4], 5)
Out[12]: 15
sort函数
sorted(...)
sorted(iterable, cmp=None, key=None, reverse=False) --> new sorted list
In [13]: sorted([1,5,2,3,9])
Out[13]: [1, 2, 3, 5, 9]
In [14]: sorted([1,5,2,4,8],reverse=1)
Out[14]: [8, 5, 4, 2, 1]
partial函数
把一个函数的某些参数设置默认值,返回一个新的函数,调用这个新函数会更简单
import functools
def showLogs(*args,**kwargs):
print(args)
print(kwargs)
func1 = functools.partial(showLogs,"a","b","c")
func1()
func1(3,4,5)
func1(key1='mikejing', key2='cjj')
func2 = functools.partial(showLogs,a="iii",b="ccc")
func2()
func2(3,6)
func2(a="444",d="666")
('a', 'b', 'c')
{}
('a', 'b', 'c', 3, 4, 5)
{}
('a', 'b', 'c')
{'key1': 'mikejing', 'key2': 'cjj'}
()
{'a': 'iii', 'b': 'ccc'}
(3, 6)
{'a': 'iii', 'b': 'ccc'}
()
{'a': '444', 'b': 'ccc', 'd': '666'}
wraps函数
import functools
def note(func):
"note function"
# 不加这句话下面的test打印的是wrapper的方法,不然打印的都是正常方法
@functools.wraps(func)
def wrapper():
"wrapper function"
print("note task insert")
return func()
return wrapper
@note
def test():
"test function"
print("task1")
test()
print(test.__doc__)
# 不加
# note task insert
# task1
# wrapper function
# ***Repl Closed***
# 加
note task insert
task1
test function
***Repl Closed***
标准库 | 说明 |
---|---|
builtins | 内建函数默认加载 |
os | 操作系统接口 |
sys | Python自身的运行环境 |
functools | 常用的工具 |
json | 编码和解码 JSON 对象 |
logging | 记录日志,调试 |
multiprocessing | 多进程 |
threading | 多线程 |
copy | 拷贝 |
time | 时间 |
datetime | 日期和时间 |
calendar | 日历 |
hashlib | 加密算法 |
random | 生成随机数 |
re | 字符串正则匹配 |
socket | 标准的 BSD Sockets API |
shutil | 文件和目录管理 |
glob | 基于文件通配符搜索 |
import hashlib
m = hashlib.md5()
print(m)
m.update("task".encode("utf-8"))
print(m.hexdigest())
478f3a4c51824ad23cb50c1c60670c0f
***Repl Closed***
扩展库 | 说明 |
---|---|
requests | 使用的是 urllib3,继承了urllib2的所有特性 |
urllib | 基于http的高层库 |
scrapy | 爬虫 |
beautifulsoup4 | HTML/XML的解析器 |
celery | 分布式任务调度模块 |
redis | 缓存 |
Pillow(PIL) | 图像处理 |
xlsxwriter | 仅写excle功能,支持xlsx |
xlwt | 仅写excle功能,支持xls ,2013或更早版office |
xlrd | 仅读excle功能 |
elasticsearch | 全文搜索引擎 |
pymysql | 数据库连接库 |
mongoengine/pymongo | mongodbpython接口 |
matplotlib | 画图 |
numpy/scipy | 科学计算 |
django/tornado/flask | web框架 |
xmltodict | xml 转 dict |
SimpleHTTPServer | 简单地HTTP Server,不使用Web框架 |
gevent | 基于协程的Python网络库 |
fabric | 系统管理 |
pandas | 数据处理库 |
scikit-learn | 机器学习库 |
match是从左到右匹配
search是从中查找
r“xxxxxx” r避免转义,直接使用原始字符串
import re
result = re.match("mkj","aaaamkjbbbb")
print(result) # None
import re
result = re.match("mkj","mkjbbbb")
print(result.group()) # mkj
标识字符
字符 | 功能 |
---|---|
. | 匹配任意1个字符(除了\n) |
[ ] | 匹配[ ]中列举的字符 [^1-3a-z] []里面跟着^,代表除了后面出现的字符以外的字符 |
\d | 匹配数字,即0-9 |
\D | 匹配非数字,即不是数字 |
\s | 匹配空白,即 空格,tab键 |
\S | 匹配非空白 |
\w | 匹配单词字符,即a-z、A-Z、0-9、_ |
\W | 匹配非单词字符 |
表示数量
* | 匹配前一个字符出现0次或者无限次,即可有可无 |
+ | 匹配前一个字符出现1次或者无限次,即至少有1次 |
? | 匹配前一个字符出现1次或者0次,即要么有1次,要么没有 |
{m} | 匹配前一个字符出现m次 |
{m,} | 匹配前一个字符至少出现m次 |
{m,n} | 匹配前一个字符出现从m到n次 |
标识原始字符串
Python中字符串前面加上 r 表示原生字符串
,
与大多数编程语言相同,正则表达式里使用"\"作为转义字符
,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符"\",那么使用编程语言表示的正则表达式里将需要4个反斜杠"\\":前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。
Python里的原生字符串很好地解决了这个问题,有了原始字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。
>>> ret = re.match(r"c:\\a",mm).group()
>>> print(ret)
c:\a
表示边界
字符 | 功能 |
---|---|
^ | 匹配字符串开头 |
$ | 匹配字符串结尾 |
\b | 匹配一个单词的边界 |
\B | 匹配非单词边界 |
^和$这两个在vi编辑器里面也同样是切换光标到行首或者行末
分组匹配
字符 | 功能 |
---|---|
| | 匹配左右任意一个表达式 |
(ab) | 将括号中字符作为一个分组 |
\num |
引用分组num匹配到的字符串 |
(?P |
分组起别名 |
(?P=name) | 引用别名为name分组匹配到的字符串 |
文档地址
Python3基础知识(1)
Python3面向对象(3)