python

python 学习中的一些理解

成员函数、类函数、静态函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Foo:
def plain_func(self): # 普通方法
print("plain_func")

@classmethod
def class_func(cls): # 类方法
print("class_func")

@staticmethod
def static_func(): # 静态方法
print("static_func")

# 必须实例化进行调用
foo = Foo()
foo.plain_func()
# 可通过类名进行调用
Foo.class_func()
Foo.static_func()

普通成员函数

普通成员函数是最一般的方法,从调用方式来看,普通函数只能在类的实例中被调用,而后两者可以通过类名进行调用。

类成员函数

classmethod 增加了一个 cls 参数,它引用了一个类实例。cls 类似于类中其他函数的 self 参数,例如 init(self),只不过 self 代表创建的实例对象,而 cls 代表类本身。classmethod 可以用于写一个只在类中运行而不在实例中运行的方法,直接通过类进行调用。不管这个方法是从实例调用还是从类调用,它都用第一个参数把类传递过来。对类的用户可见的功能可使用 classmethod。

好处:

  • 方法可以判断出自己是通过基类被调用,还是通过某个子类被调用;
  • 通过子类调用时,方法可以返回子类的实例而非基类的实例;
  • 通过子类调用时,方法可以调用子类的其他 classmethod。

静态函数

staticmethod 用于跟类有关系的功能但在运行时又不需要实例和类参与的情况,比如更改环境变量或者修改其他类的属性等。在通过类调用时,staticmethod 与 classmethod 对于调用者来说是不可区分的。
好处:

  • 调用时返回的是一个真正的函数,而且每次调用时返回同一个实例(classmethod 则会对基类和子类返回不同的 bound method 实例)

classmethod 与 staticmethod
这两个方法的用法是类似的,在大多数情况下,classmethod 也可以通过 staticmethod 代替,staticmethod 也可以在子类上被重写为 classmethod,反之亦然。

self

1
2
3
class Foo(object):
def x(self):
print( 'Foo')

Foo().x() 等同于 Foo.x(Foo()), 对象调用成员函数,相当于默认输入了一个对象给self。
而且self不是必须用的只是通常用法,所以有没有self并不重要。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Box(object):
def myInit(this, boxname, size, color):
print(this.__dict__)#显示为{}空字典
this.boxname = boxname
this.__dict__['aa'] = 'w'#甚至可以像字典一样操作
this.size = size
this.color = color # 自己写一个初始化函数,一样奏效,甚至不用self命名。其它函数当中用标准self

def open(this):
print('-->用自己的myself,打开那个%s,%s的%s' % (this.color, this.size, this.boxname))
print('-->用类自己的self,打开那个%s,%s的%s' % (this.color, this.size, this.boxname))

def close(this):
print('-->关闭%s,谢谢' % this.boxname)


# 经过改造,运行结果和标准初始化没区别
box=Box()
box.myInit('魔盒', '14m', '红色')
# b = Box('魔盒', '14m', '红色')#注释掉原来标准的初始化方法
box.close()
box.open() # 本来就会自动传一个self,现在传入b,就会让open多得到一个实例对象本身,print看看是什么。
print(box.__dict__) #对象会自动生成一个dict用来保存成员变量。

#输出
{}
-->关闭魔盒,谢谢
-->用自己的myself,打开那个红色,14m的魔盒
-->用类自己的self,打开那个红色,14m的魔盒
{'boxname': '魔盒', 'aa': 'w', 'size': '14m', 'color': '红色'}

super()

  1. 单继承
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class Base(object):
    def __init__(self):
    print 'Create Base'

    class A(Base):
    def __init__(self):
    Base.__init__(self)
    super(A, self).__init__() #python2和python3
    super().__init__() #python3
    print 'Create A'

    A()

    # 测试结果
    Create Base
    Create A

在单继承时,super().init()与Base.init()是一样的。super()避免了基类的显式调用。如果Base修改,A类也需要修改,使用super就好很多了。

  1. 多继承
    super与父类没有实质性的关联。在单继承时,super获取的类刚好是父类,但多继承时,super获取的是继承顺序中的下一个类。
    Python中的super()方法设计目的是用来解决多重继承时父类的查找问题,所以在单重继承中用不用 super 都没关系;一般我们在子类中需要调用父类的方法时才会这么用。

解决多继承带来的重复调用(菱形继承)、查找顺序(MRO)问题
用“父类名.属性”的方法调用出来代码维护时繁琐一点也并无不可,但Python是的继承机制是多继承,还是用这种方法来调用父类属性就会就回带来许多问题。假如有A、B、C、D这4个类,继承关系如下,我们要在各子类方法中显式调用父类的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Foo(object):
def x(self):
print( 'Foo')

class Foo1(Foo):
def x(self):
print( 'Foo1')
super(Foo1, self).x()

class Foo2(Foo):
def x(self):
print('Foo2')
super(Foo2, self).x() #调用过程中self 是Foo3()

class Foo3(Foo2,Foo1):
def x(self):
print( 'Foo3')
super(Foo3,self).x()

f = Foo3()
f.x()
print(Foo3.mro())

# 类按照mro顺序调用
输出:
Foo3
Foo2
Foo1
Foo
[<class '__main__.Foo3'>, <class '__main__.Foo2'>, <class '__main__.Foo1'>, <class '__main__.Foo'>, <class 'object'>]

如果不用super,Foo会被调用两次,这就是多继承带来的重复调用的问题。
事实上,在每个类声明之后,Python都会自动为创建一个名为“mro”的内置属性,这个属性就是Python的MRO机制生成的,该属性是一个tuple,定义的是该类的方法解析顺序(继承顺序),当用super调用父类的方法时,会按照mro属性中的元素顺序去挨个查找方法。我们可以通过“类名.mro”或“类名.mro()”来查看上面代码中Foo3类的mro属性值:

  1. 怎么用super
    super是一个类(不是方法),实例化之后得到的是一个代理的对象,而不是得到了父类,并且我们使用这个代理对象来调用父类或者兄弟类的方法。使用格式如下:
    super([type[, object-or-type]])
    将这个格式展开来就有一下几种传参方式:
    1
    2
    3
    super()
    super(type , obj)
    super(type_1 , type_2)

3.1 super(type , obj)
先说super(type , obj),这个方式要传入两个常数,第一个参数type必须是一个类名,第二个参数是一个该类的实例化对象,不过可以不是直接的实例化对象,该类的子类的实例化对象也行。在上文中已经说到,super会按照mro属性中的顺序去查找方法,super(type , obj)两个参数中type作用是定义在mro数组中的那个位置开始找,obj定义的是用哪个类的mro元素。

3.2 super()
super()事实上是懒人版的super(type , obj),这种方式只能用在类体内部,Python会自动把两个参数填充上,type指代当前类,obj指导当前类的实例对象,相当于super(class , self)。所以,以下三种代码是完全等效的:

1
2
3
super().fun()
super(B , self).fun()
super(__class__ , self).fun()

3.3 super(type_1 , type_2)
当super传入的两个参数都是类名是,type_2必须是type_1的子类。

1
2
print(super(F , F()).fun()) #输出结果为:D.fun
print(super(F , F).fun()) # 报错:TypeError: fun() missing 1 required positional argument: 'self'

所以,super(type_1 , type_2)与super(type , obj)有区别,在看一下下列输出:

1
2
3
print(super(F , F()).fun)# 输出结果:<bound method D.fun of <__main__.F object at 0x000001BD44A98B38>>
print(super(F , F).fun) # 输出结果:<function D.fun at 0x000001BD44A9EE18>
print(D.fun) # 输出结果:<function D.fun at 0x000001BD44A9EE18>

所以,当super传入的两个传输都是类时,得到的就是一个指向继承顺序下的类的代理,并未绑定实例,要调用D类的fun方法,还需传入实例:
print(super(F , F).fun(F())) #输出结果:D.fun
所以,当super传入的两个参数都是类的时候,最好只用来调用类的静态方法或者类方法。

参考

3.4 避免使用 super(self.class, self),一般情况下是没问题的,就是怕极端的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Foo(object):
def x(self):
print 'Foo'

class Foo2(Foo):
def x(self):
print 'Foo2'
super(self.__class__, self).x() # wrong

class Foo3(Foo2):
def x(self):
print 'Foo3'
super(Foo3, self).x()

f = Foo3()
f.x()

在 Foo2 中的 super(self.class, self) 导致了死循环,super 永远去找 Foo3 的 MRO 中的下一个类,super 的第一个参数应该总是当前的类。

global 和 nolocal

global

函数外部定义的变量(即global范围),在函数内部可以引用,但是不能修改。

1
2
3
4
5
6
7
8
a = 1234
def myfun1():
print(a)
a = 123 #UnboundLocalError: local variable 'a' referenced before assignment
print(a)
myfun1()
print(a)
# 运行会报错

1
2
3
4
5
6
7
8
9
10
11
12
a = 1234
def myfun1():
global a
print(a)
a = 123
print(a)
myfun1()
print(a)
# 输出
1234
123
123

nolocal

指定在当前作用域使用上层作用域中(但排除global作用域)的变量名

1
2
3
4
# nonlocal只能在函数内的函数中使用,如果直接在全局作用域下定义的函数中使用该语句,会报错
def fun():
nonlocal y
# SyntaxError: no binding for nonlocal 'y' found

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def outer():
n='n'
print(n)
def inner():
nonlocal n
n='nn'
print(n)
inner()
print(n)
outer()
# 输出

nn
nn
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#对比修改为global
n = 'nnn'
def outer():
n='n'
print(n)
def inner():
global n
n='nn'
print(n)
inner()
print(n)
outer()
print(n)
# 输出

nn
n # 第一层函数里的n没有被修改
nn #最外层的n被修改了

list

list的底层原理

list.sort()的底层原理(TimSort) 

参考

生成器、迭代器、装饰器

闭包

__init() ,__new(), __call__()的作用

python filter() and map()

args *kwargs的含义与用法

垃圾回收机制

内存管理,内存池最大多少

yield是什么,与return的区别

copy、deepcopy和赋值的区别

变量前面一个下划线、变量前面两个下划线、前面和后面各一个下划线(var、__var、_var

lambda与def 定义函数的区别

函数的参数传递

read readline readlines的区别

python 的数据类型有哪些?

python如何用一条语句去除列表中的空字符串

Contents
  1. 1. 成员函数、类函数、静态函数
    1. 1.1. 普通成员函数
    2. 1.2. 类成员函数
    3. 1.3. 静态函数
  2. 2. self
  3. 3. super()
  4. 4. global 和 nolocal
    1. 4.1. global
    2. 4.2. nolocal
  5. 5. list
    1. 5.1. list的底层原理
    2. 5.2. list.sort()的底层原理(TimSort) 
  6. 6. 生成器、迭代器、装饰器
  7. 7. 闭包
  8. 8. __init() ,__new(), __call__()的作用
  9. 9. python filter() and map()
  10. 10. args *kwargs的含义与用法
  11. 11. 垃圾回收机制
  12. 12. 内存管理,内存池最大多少
  13. 13. yield是什么,与return的区别
  14. 14. copy、deepcopy和赋值的区别
  15. 15. 变量前面一个下划线、变量前面两个下划线、前面和后面各一个下划线(var、__var、_var)
  16. 16. lambda与def 定义函数的区别
  17. 17. 函数的参数传递
  18. 18. read readline readlines的区别
  19. 19. python 的数据类型有哪些?
  20. 20. python如何用一条语句去除列表中的空字符串
|