python面试题之我见(1)
2017-05-26 10:40 核桃不是桃 阅读(183) 评论(0) 收藏 举报原面试题链接:很全的python面试题
Python语言特性
1 python的函数参数传递:
两个例子:
1 a = 1 2 def fun(a): 3 a = 2 4 fun(a) 5 print a # 1
1 a = [] 2 def fun(a): 3 a.append(1) 4 fun(a) 5 print a # [1]
理解这个问题,首先要清楚在python中所有变量都是对象的“引用”。对象分为“可变”(mutable)和“不可变”(immutable)。
因此将一个可变对象传给方法时,该方法将获得对同一个对象的引用。但如果将一个不可变对象传递给方法,那么就无法获得对同一个对象的引用。
这里举一个例子,列表是可变对象:
1 def try_to_change_list_contents(the_list): 2 print('got', the_list) 3 the_list.append('four') 4 print('changed to', the_list) 5 6 outer_list = ['one', 'two', 'three'] 7 8 print('before, outer_list =', outer_list) 9 try_to_change_list_contents(outer_list) 10 print('after, outer_list =', outer_list)
这时得到的输出是:
1 before, outer_list = ['one', 'two', 'three'] 2 got ['one', 'two', 'three'] 3 changed to ['one', 'two', 'three', 'four'] 4 after, outer_list = ['one', 'two', 'three', 'four']
可见传入的参数是对其的引用outer_list,而不是它的副本。
假如我们尝试改变作为参数传递的引用时会发生什么?
def try_to_change_list_reference(the_list): print('got', the_list) the_list = ['and', 'we', 'can', 'not', 'lie'] print('set to', the_list) outer_list = ['we', 'like', 'proper', 'English'] print('before, outer_list =', outer_list) try_to_change_list_reference(outer_list) print('after, outer_list =', outer_list)
输出:
before, outer_list = ['we', 'like', 'proper', 'English'] got ['we', 'like', 'proper', 'English'] set to ['and', 'we', 'can', 'not', 'lie'] after, outer_list = ['we', 'like', 'proper', 'English']
由于the_list参数是通过☞传递的,所以给它分配一个新的列表并不影响方法外的代码。
字符串是不可变对象:
我们尝试改变参数:
def try_to_change_string_reference(the_string): print('got', the_string) the_string = 'In a kingdom by the sea' print('set to', the_string) outer_string = 'It was many and many a year ago' print('before, outer_string =', outer_string) try_to_change_string_reference(outer_string) print('after, outer_string =', outer_string)
输出:
before, outer_string = It was many and many a year ago got It was many and many a year ago set to In a kingdom by the sea after, outer_string = It was many and many a year ago
可以看出,这种通过值传递参数的方式,和上文中的列表类似,都是没有办法改变原变量所指向的引用,而是在方法内部创建了一个新的引用。
那么如何解决这个问题?
第一种方式是使用返回值:
def return_a_whole_new_string(the_string): new_string = something_to_do_with_the_old_string(the_string) return new_string # then you could call it like my_string = return_a_whole_new_string(my_string)
如果想避免使用返回值,另一种方式是,创建一个类来保存值,并将其传递到函数中或者使用现有的类。比如一个列表:
def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change): new_string = something_to_do_with_the_old_string(stuff_to_change[0]) stuff_to_change[0] = new_string # then you could call it like wrapper = [my_string] use_a_wrapper_to_simulate_pass_by_reference(wrapper) do_something_with(wrapper[0])
但这种方式似乎有点儿麻烦。
2 python中的元类metaclass
简而言之,就是类也是对象,元类就是“类的类”
比如代码片:
class ObjectCreator(object): pass
将在内存中创建一个对象,名字就是ObjectCreator。这个对象(类)自身拥有创建对象(类实例)的能力,这就是为什么它是一个类的原因。但其本质上依然是一个对象,因此可以对其进行如下操作:
- 将其赋值给一个变量
- 拷贝
- 增加属性
- 可以将其作为函数参数进行传递
>>> print ObjectCreator # 你可以打印一个类,因为它其实也是一个对象 <class '__main__.ObjectCreator'> >>> def echo(o): … print o … >>> echo(ObjectCreator) # 你可以将类做为参数传给函数 <class '__main__.ObjectCreator'> >>> print hasattr(ObjectCreator, 'new_attribute') Fasle >>> ObjectCreator.new_attribute = 'foo' # 你可以为类增加属性 >>> print hasattr(ObjectCreator, 'new_attribute') True >>> print ObjectCreator.new_attribute foo >>> ObjectCreatorMirror = ObjectCreator # 你可以将类赋值给一个变量 >>> print ObjectCreatorMirror() <__main__.ObjectCreator object at 0x8997b4c>
3 @staticmethod和@classmethod
python中包含三种方法,分别是静态方法(staticmethod)类方法(classmethod)和实例方法,如下:
def foo(x): print "executing foo(%s)"%(x) class A(object): def foo(self,x): print "executing foo(%s,%s)"%(self,x) @classmethod def class_foo(cls,x): print "executing class_foo(%s,%s)"%(cls,x) @staticmethod def static_foo(x): print "executing static_foo(%s)"%x a=A()
实例方法和类方法类似,实例方法传入参数(self),是对实例的绑定,类方法传入参数(cls)是对类的绑定。
调用实例方法时,就是调用了实例的方法,类方法,就是调用了类的方法。
注意这里的参数self和cls可以替换称其他参数,但是python约定最好不要改。
静态方法和普通方法一样,只是不需要跟谁绑定,并且在调用时需要使用a.static_foo(x)或者A.static_foo(x)来调用.
| 、 | 实例方法 | 类方法 | 静态方法 |
| a = A() | a.foo(x) | a.class_foo(x) | a.static_foo(x) |
| A | 不可用 | A.class_foo(x) | A.static_foo(x) |
4 类变量和实例变量
class Person: name="aaa" p1=Person() p2=Person() p1.name="bbb" print p1.name # bbb print p2.name # aaa print Person.name # aaa
类变量就是供类使用的变量,实例变量就是供实例使用的.
这个问题其实又回到第一个问题,实际上就是一个参数传递的问题。
所以如果是一个可变对象的话,比如一个列表。那么:
class Person: name=[] p1=Person() p2=Person() p1.name.append(1) print p1.name # [1] print p2.name # [1] print Person.name # [1]
这样看起来问题就简单多了。
5 Python自省
简而言之就是,运行时能够获得对象的类型,比如type(),dir(),getattr(),hasattr(),isinstance()
6 字典推导式(字典生成式)
字典可以如同列表一样使用生成式:
d = {key: value for (key, value) in iterable}
7 Python中单下划线和双下划线:
- __foo__:一种约定,Python内部的名字,用来区别其他用户自定义的命名,以防冲突。
- _foo:一种约定,用来表示变量私有(但实际上并不是真正的私有)
- __foo:这个有真正的意义:解析器用
_classname__foo来代替这个名字,以区别和其他类相同的命名.
8字符串格式化:%和.format
.format在许多方面看起来更便利,%最烦人的地方在于无法同时传递一个变量和元祖。
9 迭代器与生成器
据说是stackoverflow中python排名第一的问题,链接可以看这里
浙公网安备 33010602011771号