原文发表在个人博客主页,转载请注明出处!python
建议二十八:区别对待可变对象和不可变对象
python中一切皆对象,每个对象都有一个惟一的标识符(id())、类型(type())以及值,对象根据其值可否修改分为可变对象和不可变对象,其中数字、字符串、元组属于不可变对象,字典以及列表、字节数组属于可变对象。
来看一段程序:数组
class Student(object): def __init__(self,name,course=[]): self.name = name self.course = course def addcourse(self,coursename): self.course.append(coursename) def printcourse(self): for item in self.course: print item xl = Student('xl') xl.addcourse('computer') xl.addcourse('automation') print xl.name + "'s course:" xl.printcourse() print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~" cyj = Student('cyj') cyj.addcourse('software') cyj.addcourse('NLP') print cyj.name + "'s course:" cyj.printcourse()
运行结果会让初学者大吃一惊:数据结构
xl's course: computer automation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ cyj's course: computer automation software NLP
经过查看xl和cyj的course变量的id,发现他们的值是同样的,即指向内存中的同一块地址,可是xl和cyj倒是两个不一样的对象。在实例化这两个对象的时候,这两个对象被分配了不一样的内存空间,而且调用init()函数进行初始化,但因为init()函数的第二个参数是个默认参数,默认参数在函数调用的时候仅仅被评估一次,之后都会使用第一次评估的结果,所以实际上对象空间里面course所指向的是list的地址,这时咱们在将可变对象做为默认参数的时候要警戒的,对可变对象的更改会直接影响原对象,能够用以下方式解决:app
def __init__(self,name,course=None): self.name = name if course is None:course = [] self.course = course
对于不可变对象来讲,当咱们对其进行相关操做的时候,python实际上仍然保存原来的值,从新建立一个新的对象。当有两个对象同时指向一个字符串对象的时候,对其中一个对象的操做并不会影响另外一个对象。好比:函数
str1 = "write pythonic code" str2 = str1 str1 = str1[-4:] print id(str1) print id(str2) print str1 print str2
建议二十九:和{}:一致性容器初始化形式
列表是一个颇有用的数据结构,因为其灵活性在实际应用中被普遍使用。对于列表来讲,列表解析十分经常使用。
列表解析的语法以下,它迭代iterable中的每个元素,当条件知足的时候便根据表达式expr计算的内容生成一个元素并放入新的列表中,依次类推,最终返回整个列表。大数据
[expr for iter_item in iterable if cond_expr]
列表解析的使用很是灵活:ui
nested_list = [['Hello', 'World'],['Goodbye', 'World']] nested_list = [[ele.upper() for ele in word] for word in nested_list]
[(a,b) for a in ['1', '2', '3', '4'] for b in ['a', 'b', 'c', 'd'] if a != b]
def f(v): if v%2 == 0: v = v ** 2 else: v = v + 1 return v print [f(v) for v in [1,2,3,-1] if v > 0] print [v ** 2 if v %2 == 0 else v + 1 for v in [1,2,3,-1] if v > 0]
fp = open('wdf.py','r') res = [i for i in fp if 'weixin' in i] print res
为何要推荐在须要生成列表的时候使用列表解析呢?debug
除了列表可使用列表解析的语法以外,其余内置的数据结构也支持,以下:code
#generator (expr for iter_item in iterable if cond_expr) #set {expr for iter_item in iterable if cond_expr} #dict {expr1: expr2 for iter_item in iterable if cond_expr}
建议三十:记住函数传参既不是传值也不是传引用
以往关于python中函数传参数有三种观点:orm
这些理解都是有些误差的,python中的赋值与咱们所理解的C/C++等语言的赋值的意思并不同。以以下语句为例来看C/C++和python是如何运做的
a = 5, b= a, b = 7
C/C++中当执行b=a的时候,在内存中申请一块内存并将a的值复制到该内存中,当执行b=7以后是将b对应的值从5修改到7
python中赋值并非复制,b=a操做使得b与a引用同一对象,而b=7则是将b指向对象7
所以,对于python函数参数既不是传值也不是传引用,应该是传对象或者说传对象的引用。函数参数在传递的过程当中将整个对象传入,对可变对象的修改在函数外部以及内部均可见,调用者和被调用者之间共享这个对象,而对于不可变对象,因为并不能真正被修改,所以,修改每每是经过生成一个新对象而后赋值来实现的。
建议三十一:慎用变长参数
python支持可变长度的参数列表,能够经过在函数定义的时候使用*args和**kwargs这两个特殊语法来实现。
使用args来实现可变参数列表:args用于接收一个包装为元组形式的参数列表来传递非关键字参数,参数个数能够任意:
def sumf(*args): res = 0 for x in args[:]: res += x return res print sumf(2,3,4) print sumf(1,2,3,4,5)
使用**kwargs接收字典形式的关键字参数列表,其中字典的键值分别表示不可变参数的参数名和值:
def category_table(**kwargs): for name, value in kwargs.items(): print '{0} is a kind of {1}'.format(name, value) category_table(apple = 'fruit', carrot = 'vegetable')
当普通参数,默认参数,和上述两种参数同时存在的时候,会优先给普通参数和默认参数赋值,为何要慎用可变长参数呢?
参数三十二:深刻理解str()和repr()的区别
这两个方法均可以将python中的对象转换为字符串,他们的使用以及输出都很是类似,区别呢?
obj == eval(repr(obj))
建议三十三:分清staticmethod和classmethod的使用场景
python中的静态方法(staticmethod)和类方法(classmethod)都依赖于装饰器来实现,用法以下:
#staticmethod class C(object): @staticmethod def f(arg1, arg2, ...): #classmethod class C(object): @classmethod def f(arg1, arg2, ...):
静态方法和类方法均可以经过类名.方法名或者实例.方法名的形式来访问。其中静态方法没有常规方法的特殊行为,如绑定、非绑定、隐式参数等规则,而类方法的调用使用类自己做为其隐含参数,但调用自己并不须要显示提供该参数。
那为何须要静态方法和类方法呢?假设有水果类Fruit,它用属性total表示总量,用set()来设置重量,print_total()方法来打印水果数量。类Apple和类Orange继承自Fruit,现须要分别跟踪不一样类型的水果的总量,实现方法汇总:
class Fruit(object): total = 0 def print_total(cls): print cls.total @classmethod def set(cls, value): cls.total = value class Apple(Fruit): pass class Orange(Fruit): pass app1 = Apple() app1.set(200) app2 = Apple() org1 = Orange() org1.set(300) org2 = Orange() app1.print_total() org1.print_total()
简单分析可知,针对不一样种类的水果对象调用set()方法的时候隐形传入的参数为该对象所对应的类,在调用set()的过程当中动态生成了对应的类的类变量。
静态方法通常适用于既不跟特定的实例相关也不跟特定的类相关的方法。他存在于类中,较以外部函数可以更加有效的将代码组织起来,从而使相关代码的垂直距离更近,提升代码的可维护性。
参考:编写高质量代码--改善python程序的91个建议