1. is 和 ==区别
is 判断是否是一个ID(内存中的数据是否是同一个), == 判断内容是否一致。
2.python 常量池包括
1.短整型的-5~256
2.字符串的数字、大小写字母随意组合(待研究)
3.字符串的单个符号
4.布尔类型的True、False
5.None
6.空元组tuple
7.空不可变集合frozenset
3.在同一个作用域内的两个变量,如果赋值超出常量池的整形、字符串、浮点型,则使用同一个内存。
# 在命令行中 a = 1 b = 1 print(a is b) # True a = 257 b = 257 print(a is b) # False a = 'helloWorld' b = 'helloWorld' print(a is b) # True a = 'hello world' b = 'hello world' print(a is b) # False a = 'hello world';b = 'hello world';print(a is b) # True # 在同一个作用域中,如同一个函数内。 # 以上都是 True # a和b不在同一个函数、类、py文件中
# 结果与在命令行中的结果相同
4.可变对象与不可变对象
不可变对象:该对象所指向的内存中的值不能被改变。也就是说内存中一旦一个值占据之后,这块内存中存的数据就不会发生变化了,直到被销毁后才可以存其它值。
可变对象:该对象所指向的内存中的值可以被改变。
不可变对象类型:bool、int、float、str、tuple、frozenset
可变对象类型:list、set、dict
常量池中的数据类型都是不可变对象类型!
5.值传递与引用传递
1. 不可变对象作为函数参数,相当于C语言的值传递。
2. 可变对象作为函数参数,相当于C语言的引用传递。(因列表是可变数据类型,作为参数传递实则是传递对列表的引用,修改更新列表值后依旧引用不变)
# 值传递,value_a把自己的值传递给函数func_1的变量value_1后, # value_1的值修改了,value_a的值不会变 def func_1(value_1): print(value_1) value_1 = 2 print(value_1) value_a = 1 func_1(value_a) print(value_a) # 引用传递,list_a把引用传递给函数func_2的变量list_1后, # 修改list_1的值,list_a的值也发生的改变。 # 所以,如果list_a要再给别的函数传的话,就是func_2修改后的值 def func_2(list_1): list_a[0] = 2 print(list_1) list_a = [1, 2] func_2(list_a) print(list_a) # 引用传递,当第一次调用func_3时,由于没有入参,默认从内存开辟 # 一块空间将一个空列表放进去,然后传给list_2。当list_2修改后 # 空间中的值发生变化。第二次调用func_3的时候,将第一次开辟空间的引用传给了 # list_2,所以第二次的list_2有两个hello。如果是3个线程的话,如果每个线程里面 # 循环添加100个hello,当线程结束时list_2的值可能是100到300个hello。 def func_3(list_2=[]): list_2.append('hello') print(list_2) func_3() func_3()
6.深拷贝与浅拷贝
浅拷贝:内存中的值只有一份,创建一个新的引用,指向这个内存,将新引用传递给新对象。
深拷贝:将内存中的值拷贝一份,开辟一个新空间存进去,将新空间的引用传递给新对象。
浅拷贝的目的:节约内存。
深拷贝的目的:防止变量a的值修改时,影响变量b的值。
深拷贝是针对对可变对象的,不可变对象不适用,因为不可变量的值不会改变。如果变量a的值是个不可变对象,当变量a修改时他的引用指向了新的内存地址。
import copy a = [1, 2] b = a # 赋值操作是浅拷贝 print('a_id=%s, b_id=%s, b_value=%s' % (id(a), id(b), b)) a[0] = 'a' print('a_id=%s, b_id=%s, b_value=%s' % (id(a), id(b), b)) # 变量a修改时影响了变量b a = [1, 2] b = copy.deepcopy(a) # b=a[:]也是深拷贝 print('a_id=%s, b_id=%s, b_value=%s' % (id(a), id(b), b)) a[0] = 'a' print('a_id=%s, b_id=%s, b_value=%s' % (id(a), id(b), b)) # 当a修改时不会影响b a = 'hello wold' b = copy.deepcopy(a) print('a_id=%s, b_id=%s, b_value=%s' % (id(a), id(b), b)) # 因为a是不可变对象,所以即使深拷贝也不用开辟新空间,所以ab的id相同 a = 'hello agu' print('a_id=%s, b_id=%s, b_value=%s' % (id(a), id(b), b)) # 当a修改后a的引用指向了新的内存空间,所以a的id改变了
7.私有化与property
- 为了让外部无法访问特性或方法,只要在名字前面加上双下划线即可。
- python本质上并不完全支持私有化,只不过是将名字进行改变了
class Secret(object): __name = 'yoda' # 加上双下划线私有化特性 def get_name(self): return self.__name def __secret(self): # 私有化方法 print "can't find" def access(self): return self.__secret() # 访问器访问私有方法 A2 = Secret() print A2.__name # 访问不到特性! 要抛异常 print A2._Secret__name # 还是可以从外部访问特性 A2.__secret() # 访问不到特性! 要抛异常 A2._Secret__secret()
- 有一些面向对象语言支持私有特性,这些特性无法从外部直接访问,需要编写getter和setter方法对这些特性进行操作
- python不需要getter和seter方法,因为python中所有特性都是公开的,如果不放心使用直接访问对象的特性,可以为对象编写setter和getter方法,但是更好的解决办法是使用属性(property)
- python隐藏访问器的方法,让所有特性看起来一样,这种通过访问器定义的特性被称为属性
- property()第一个参数是getter方法,第二个参数是setter方法
- property函数可以用0,1,2,3或者4个参数来调用,如果没有参数,产生的属性既不可读,也不可写,这四个参数分别叫做fget,fset,fdel,fdoc,
如果想要一个属性是只写的,并且有一个文档字符串,能使用他们作为关键字参数
# 第一种方法使用属性 class Foo(object): def __init__(self): self.name = 'yoda' self.work = 'master' def get_person(self): return self.name, self.work def set_person(self, value): self.name, self.work = value person = property(get_person, set_person) # 创建一个新属性person A3 = Foo() print A3.person # 输出:('yoda', 'master') A3.person = 'skylaer', 'programer' # A3.set_person(('skylaer', 'programer111')) # 入参必须元组 print A3.person # 输出:('skylaer', 'programer')
# 第二种方法使用属性 class Foo: def __init__(self): self.name = 'yoda' self.work = 'master' @property def person(self): return self.name, self.work @person.setter # 如果不指定stter属性,那么无法从类的外部对它的值进行设置,这对于只读特性非常有用 def person(self, value): self.name, self.work = value A3 = Foo() print(A3.person) A3.person = 'skylaer', 'programer' print(A3.person)
8.列表生成式
range(1,100,5) # 第一个参数表示开始位,第二个参数表示结束位(不含),第三个参数表示步长,就是每5个数返回一次。 a = [i for i in range(1,10)] # 列表生成式表示返回i的值,并且返回9次,每次返回的是i的值。 a = [2 for i in range(1,10)] # 这里表示返回2,并且返回9次,但是每次的值都是2。 a = [i for i in range10 if i%2==0] #表示在生成式内部加入if判断,当i除以2的余数等于0的时候将数值返回。 a = [(i,j) for i in range(5) for j in range(5)] #表示将i和j的值以元组为元素的形式返回,当i循环一次的时候j循环5次,以此类推
9.生成器
def num(): """ 1.生成器关键字yield,有yield的关键字的代码块就是yield的生成器 2.生成器不可以return 3.执行next时程序运行到yield行等号右边的时候就会停止, 当在次使用next的时候,将会把一个None赋值给temp,因为b的值已经在上轮循环中输出。 4.使用num().send()方法会将一个新的值赋值给temp,然后接着执行一个next """ value = 0 for i in range(6): value += 1 print('num: before yield') temp = yield value # print('num: after yield temp=%s' % temp) # next时,从此处开始,然后判断for有无结束 a = num() # 将生成器赋值给变量a。 print('---- start test ----') rst = next(a) print('---- rst=%s ---- ' % rst) rst = next(a) print('---- rst=%s ---- ' % rst) rst = a.send('666') # send时num生成器会把666赋值给temp,然后接着执行一个next print('---- after send rst=%s---- ' % rst) rst = next(a) print('---- rst=%s ---- ' % rst) i = 1000 for n in a: # 生成器可以使用for循环使用,并且不会出错。 print('call: before send, n=%s' % n) a.send(i) print('call: after send') i += 1
10.迭代器
# for i in '',[],(),{},{:} # 可以for循环的对象是可迭代对象。 a = (i for i in range(100)) # 列表生成式,把中括号改为小括号就可以变为一个列表生成器,是可迭代对象。 from collections import Iterable # 如果想验证是否是可迭代对象,可以使用isinstance()判断是否是可迭代对象。 isinstance('abc', Iterable) # 判断语法 a = [1, 2, 3, 4, 5] b = iter(a) # 使用iter()方法可以将可迭代的对象转换为可迭代对象。
11.闭包
def num(num): # 定义函数 def num_in(num_2): # 定义函数 return num + num_2 # 返回两个参数的和。 return num_in # 返回内部函数的引用。(变量名) a = num(100) # 将参数为100的函数num接收,并赋值给a,只不过这个返回值是一个函数的引用。等于 a = num_in,注意这里接收的不光是函数本身,还有已经传递的参数。 b = a(100) # 调用函数a,即num_in,并传递一个参数100,返回值给b。 print(b)
12.装饰器
- 装饰不带参数的函数
def wraps(func): # 定义了一个闭包 def func_in(): # 闭包内的函数 print('这里是需要装饰的内容,就是需要添加的内容') func() # 调用实参函数。 return func_in def test(): # 需要被装饰修改的函数。 print('无参函数的测试') test = wraps(test) # 装饰器的原理就是将原有的函数名重新定义为以原函数为参数的闭包。 test() # 这里再次掉用test()的时候,其实是将会调用闭包内的函数func_in()。所以将会起到装饰修改的作用,最后会再次调用原函数test()。 @wraps # 装饰器的python写法,等价于test = function(test),并且无需调用当代码运行道这里,Python会自动运行。 def test(): print('无参函数的测试') test() # 这里再次调用函数时,将会产生修改后的效果。
- 装饰带参数的函数
def wraps(func): # 定义了一个闭包 def func_in(*args, **kwargs): # 闭包内的函数,因为装饰器运行的实则是闭包内的函数,所以这里将需要有形参用来接收原函数的参数。 print('这里是需要装饰的内容,就是需要添加的内容: %s' % str(args)) a, b = args a += 10 b += 10 func(a, b) # 调用实参函数,并传入一致的实参。 return func_in @wraps # 装饰器的python写法,等价于test = function(test) . def test(a, b): print('a+b=%s' % (a+b)) test(1, 1) # 这里再次掉用test()的时候,其实是将会调用闭包内的函数func_in()。所以将会起到装饰修改的作用,最后会再次调用原函数test()。
- 装饰带返回值的函数
def wraps(func): # 定义了一个闭包 def func_in(*args, **kwargs): # 因为装饰器运行的实则是闭包内的函数,所以这里将需要有形参用来接收原函数的参数。 print('这里是需要装饰的内容,就是需要添加的内容') num = func(*args, **kwargs) # 调用实参函数,并传入一致的实参,并且用变量来接收原函数的返回值, return num # 将接受到的返回值再次返回到新的test()函数中。 return func_in @wraps def test(a, b): # 定义一个函数 return a + b # 返回实参的和 print(test(1, 1))
- 带参装饰器
def outer(number): def wraps(func): # 定义了一个闭包 def func_in(a, b): # 闭包内的函数 a += number b += number num = func(a, b) # 调用实参函数,并传入一致的实参,并且用变量来接收原函数的返回值, return num # 将接受到的返回值再次返回到新的test()函数中。 return func_in return wraps @outer(100) # 这里会先运行函数wraps,传入参数,之后会再次运行闭包函数进行装饰。 def test(a, b):return a + b print(test(1, 1))
- 装饰器类
class Test(object): # 定义一个类 def __init__(self, func): self.__func = func def __call__(self): # 定义call方法,当直接调用类的时候,运行这里。 print('这里是装饰的功能') self.__func() @Test # 类装饰器等于test = Test(test),将函数test当作参数传入类中的init方法,并将函数名赋值给私有属性__func,当函数test被调用的时候,其实是运行Test类中的call方法. def test(): print('被装饰的函数') test() # 这里调用的不在是函数test,而是实例对象test的call方法,会先进行装饰,然后再调用私有属性__func(),__func 其实就是被装饰的函数test。
- 类装饰器
def outer(clss): # 类装饰器 class Inner(object): def __init__(self): print('inner init') self.clss = clss() def __getattr__(self, attr): return getattr(self.clss, attr) return Inner @outer # Zoo = outer(Zoo) class Zoo(object): def __init__(self): pass def say(self): print('hello world!') zoo = Zoo() print(zoo.__class__) zoo.say()
13. 动态添加属性和方法
class Person(object): # 创建一个类 def __init__(self, name): # 定义初始化信息。 self.name = name li = Person('李') # 实例化Person('李'),给变量li li.age = 20 # 再程序没有停止下,将实例属性age传入。动态语言的特点。 Person.age = None # 这里使用类名来创建一个属性age给类,默认值是None。Python支持的动态属性添加。 def eat(self): # 定义一个方法,不过这个方法再类之外。 print('%s正在吃东西。。' % self.name) import types # 动态添加方法需要使用tpyes模块。 li.eat = types.MethodType(eat, li) # 使用types.MethodType,将函数名和实例对象传入,进行方法绑定。并且将结果返回给li.eat变量。实则是使用一个和li.eat方法一样的变量名用来调用。 li.eat() # 调用外部方法eat()方法。 @staticmethod # 定义静态方法。 def test(): # 定义静态方法,静态方法可以不用self参数。 print('这是一个静态方法。') Person.test = test # 使用类名.方法名 = test的形式来方便记忆和使用,Person.test其实只是一个变量名,没有特殊的含义。 Person.test() # 调用test方法。 @classmethod # 类方法 def test(cls): print('这是一个类方法。') Person.test = test # 定义一个类属性等于方法名。 Person.test() # 调用方法。 class test(object): # 定义一个类。 __slots__ = ('name', 'age') # 使用slots来将属性固定,不能进行动态添加修改。
14.元类
# 元类是只使用type创建的类,使用type会有3个参数,第一个是类名,第二个小括号内是父类名,需要使用元组。 # 第三个字典中是类属性,使用type能够快速的动态创建一个类。 Test = type('Test', (object,), {'num': 0}) class Test(object): # 创建一个类,等价于上边 num = 0 def eat(self): # 定义一个函数,self作为第一个参数。 print ('%s正在吃饭。。' % self.name) # 使用type创建一个类,但是有两个属性,一个是eat,一个是name,但是eat的值是函数eat的引用。 Person = type('Person', (object,), {'eat': eat, 'name': None})
15.内建属性
__init__ #构造初始化函数,__new__之后运行 __new__ #创建实例所需的属性 __class__ #实例所在的类,实例.__class__ __str__ #实例的字符串表示,可读性高 __repr__ #实例的字符串表示,准确性高 __del__ #删除实例引用 __dict__ #实力自定义属性,vars(实例.__dict__) __doc__ #类文档,help(类或者实例) __bases__ #当前类的所有父类 __getattribute__ #属性访问拦截器。
16.内建方法
range(start,stop,[,step]) #生成器 map(function, iterable, ...) # map() 会根据提供的函数对指定序列做映射。 filter(function, iterable) #filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。 reduce(function, iterable[, initializer]) #reduce() 函数会对参数序列中元素进行累积。 sorted(iterable[, cmp[, key[, reverse]]]) #sorted() 函数对所有可迭代的对象进行排序操作。sort 与 sorted 区别: sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。 list 的 sort 方法返回的是对已经存在的列表进行操作,而内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作。