美文网首页
编写高质量代码阅读笔记

编写高质量代码阅读笔记

作者: 萍水间人 | 来源:发表于2020-02-21 23:17 被阅读0次

    不可变对象

    Python中一切皆对象,每一个对象都有一个唯一的标示符(id())、类型(type())以及值。对象根据其值能否修改分为可变对象和不可变对象,其中数字、字符串、元组属于不可变对象,字典以及列表、字节数组属于可变对象

    字符串属于不可变对象,任何对字符串的直接修改都会报错

    那怎么修改呢?通过 array 来进行

    '''
    修改字符串,引入array模块
    '''
    a = "nihao"
    
    import array
    
    b = array.array('c', a) # 注意第一个参数必须是 'c'
    # 然后将b按照字符串来修改
    b[3] = 'a'
    b.tostring() # 即可转换位字符串
    

    ==经常遇到的错误==

    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
    stuA=Student("Wang yi")
    stuA.addcourse("English")
    stuA.addcourse("Math")
    print stuA.name +"'s course:"
    stuA.printcourse()
    print "-----------------------"
    stuB=Student("Li san")
    stuB.addcourse("Chinese")
    stuB.addcourse("Physics")
    print stuB.name +"'s course:"
    stuB.printcourse()
    

    运行结果

    Wang yi's course:
    English
    Math
    -----------------------
    Li san's course:
    English
    Math
    Chinese
    Physics
    

    这是由于 init 函数第二个参数是默认参数,m默认参数在被调用的时候只初始化一次。所以两个对象的 course 是同一个值

    列表推导式

    比如实现如下代码:

    words = ['  Are', '  abandon', 'Passion','Business',' fruit  ','quit']
    size = len(words)
    newlist = []
    for i in range(size):
        if words[i].strip().istitle():
            newlist.append(words[i])
    
    print newlist
    

    功能比较简单,就是遍历 words 中的每一个元素,然后判断首字母是不是大写

    如果用列表推导式:

    words = ['  Are', '  abandon', 'Passion','Business',' fruit  ','quit']
    newlist = [ i for i in words if i.strip().istitle()]
    

    很快就能完成

    多重嵌套

    >>> nested_list = [['Hello', 'World'], ['Goodbye', 'World']]
    >>> nested_list = [[s.upper() for s in xs] for xs in nested_list]
    >>> print nested_list
    [['HELLO', 'WORLD'], ['GOODBYE', 'WORLD']]
    >>>
    

    xs 是一个list,再对xs中的每一个字符串进行操作

    多重迭代

    >>> [(a,b) for a in ['a','1',1,2] for b in ['1',3,4,'b'] if a != b]
    [('a', '1'), ('a', 3), ('a', 4), ('a', 'b'), ('1', 3), ('1', 4), ('1', 'b'), (1,
     '1'), (1, 3), (1, 4), (1, 'b'), (2, '1'), (2, 3), (2, 4), (2, 'b')]
    >>>
    

    做笛卡尔积, 去掉元素值相等的元组

    使用函数

    >>> def f(v):
    ...     if v%2 == 0:
    ...         v = v**2
    ...     else:
    ...         v = v+1
    ...     return v
    ...
    >>> [f(v) for v in [2,3,4,-1] if v>0]       #
    表达式可以是函数
    [4, 4, 16]
    >>> [v**2 if v%2 ==0 else v+1 for v in [2,3,4,-1] if v>0]#
    也可以是普通计算
    [4, 4, 16]
    >>>
    

    任何可迭代对象都是可以的

    fh = open("test.txt", "r")
    result = [i for i in fh if "abc" in i]  #文件句柄可以当做可迭代对象
    print result
    

    函数传参

    如下例子中,甚至可以直接将一个列表推导式作为参数传入其中

    >>> def foo(a):
    ...    for i in a:
    ...           print i
    ...
    >>> foo([1, 2, 3])
    1
    2
    3
    >>> foo(i for i in range(3) if i % 2 == 0)
    0
    2
    

    字典推导式

    互换 key 和 value

    person = {'name':'xiaobai','city':'paris'}
    person_reverse = {v:k for k,v in person.items()}   #简单互换key和value的值即可
    

    ​ 再看一个复杂点的栗子:统计字母(不分大小写)出现的次数

    nums = {'a':10,'b':20,'A':5,'B':3,'d':4}
    
    num_frequency  = {k.lower():nums.get(k.lower(),0) + nums.get(k.upper(),0)
                      for k in nums.keys() }  # 注意 get方法中第二个参数是指定的默认值
    
    print(num_frequency)
    

    字典推导式配合枚举:将list转换位dict

    fruit = ['apple','organge','banana','mango','peach']
    
    fruit_positon = {v:i for i,v in enumerate(fruit)}
    print(fruit_positon)
    
    Out: {'apple': 0, 'organge': 1, 'banana': 2, 'mango': 3, 'peach': 4}
    

    集合推导式

    返回人名

    names = [ 'Bob', 'JOHN', 'alice', 'bob', 'ALICE', 'James', 'Bob','JAMES','jAMeS' ]
    names_standard = { n[0].upper()+n[1:].lower() for n in names}
    
    print(names_standard)
    Out: {'John', 'Bob', 'James', 'Alice'}
    

    综合运用:统计一个文本中的单词长度大于5并且倒过来也有意义

    with open('dictionary.txt') as dictionary_file:
        words = (line.rstrip() for line in dictionary_file)
        words_over_five_letters = [w for w in words if len(w)>5 ]
    
    
    reversed_words ={
        word[::-1]
        for word in words_over_five_letters
         }
    
    reversible_words = [
        word
        for word in words_over_five_letters
        if word in reversed_words
    ]
    
    for word in reversible_words[0:20]:
        print(word)
    

    函数是如何传参的

    先下结论:函数的传参既不是传值也不是传引用,更不是有时候传值有时候传引用

    def change_me(org_list):
        print id(org_list)
        new_list = org_list
        print id(new_list)
        if len(new_list)>5:
            new_list = ['a','b','c']
        for i,e in enumerate(new_list):
            if isinstance(e,list):
                    new_list[i]="***"   #将元素为list类型的替换为***
    
    
        print new_list
        print id(new_list)
    
    test1 = [1,['a',1,3],[2,1],6]
    change_me(test1)
    print(test1)
    test2=[1,2,3,4,5,6,[1]]
    change_me(test2)
    print(test2)
    
    

    运行结果:

    64172232
    64172232
    [1, '***', '***', 6]
    64172232
    [1, '***', '***', 6]
    64121928
    64121928
    ['a', 'b', 'c']
    64018440
    [1, 2, 3, 4, 5, 6, [1]]
    

    第一个测试可以说是传引用,但是第二个测试却变成了传值

    epub_621966_21.jpg

    当org_list的长度大于5的时候,new_list =['a','b','c']操作重新创建了一块内存并将new_list指向它。当传入参数为test2=[1,2,3,4,5,6,[1]]的时候,函数的执行并没有改变该列表的值

    因此,对于Python函数参数是传值还是传引用这个问题的答案是:都不是。正确的叫法应该是传对象(call by object)或者说传对象的引用(call-by-object-reference)。函数参数在传递的过程中将整个对象传入,对可变对象的修改在函数外部以及内部都可见,调用者和被调用者之间共享这个对象,而对于不可变对象,由于并不能真正被修改,因此,修改往往是通过生成一个新对象然后赋值来实现的。

    默认参数

    默认参数可能带来的问题已经在 不可变对象 阐述过了

    >>> def appendtest(newitem,lista = []):              #默认参数为空列表
    ...    print id(lista)
    ...    lista.append(newitem)
    ...    print id(lista)
    ...    return lista
    ...
    >>>
    >>> appendtest('a',['b',2,4,[1,2]])
    39644216
    39644216
    ['b', 2, 4, [1, 2], 'a']
    >>>
    

    def在Python中是一个可执行的语句,当解释器执行def的时候,默认参数也会被计算,并存在函数的.func_defaults属性中。由于Python中函数参数传递的是对象,可变对象在调用者和被调用者之间共享,因此当首次调用appendtest(1)的时候,[]变为[1],而再次调用的时候由于默认参数不会重新计算,在[1]的基础上便变为了[1,'a']。我们可以通过查看函数的func_defaults来确认这一点。

    变长参数

    *args**kwargs 代表了变长参数

    一个简单地示例:

    def SumFun(*args):
        result = 0
        for x in args[0:]:
            result += x
        return result
    print SumFun(2,4)
    print SumFun(1,2,3,4,5)
    print SumFun()
    

    变长参数带来的问题:

    def set_axis(x,y, xlabel="x", ylabel="y", *args, **kwargs):
        pass
    

    以下调用方式都是对的:

    set_axis(2,3, "test1", "test2","test3", my_kwarg="test3")
    ①
    set_axis(2,3, my_kwarg="test3")
    set_axis(2,3, "test1",my_kwarg="test3")
    ②
    set_axis("test1", "test2", xlabel="new_x",ylabel = "new_y", my_kwarg="test3")
    set_axis(2,"test1", "test2",ylabel = "new_y", my_kwarg="test3")
    

    静态方法和类方法

    类方法实例:统计实例化了多少个对象

    思路:每次实例对象的时候必然会调用 __init__ 方法,

    class Kls(object):
        num_inst = 0
    
        def __init__(self):
            Kls.num_inst = Kls.num_inst + 1 # 每实例化一个对象变量的值就会+1
    
        @classmethod
        def get_no_of_instance(cls):
            return cls.num_inst
    
    
    ik1 = Kls()
    ik2 = Kls()
    
    print ik1.get_no_of_instance() # 输出2
    print Kls.get_no_of_instance() # 输出2
    

    书上提到的一个类方法栗子:

    class Fruit(object):
        total = 0
        @classmethod
        def print_total(cls):
            print cls.total
            #print id(Fruit.total)
            #print id(cls.total)
        @classmethod
        def set(cls, value):
            #print "calling class_method(%s,%s)"%(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()  #output 200
    org1.print_total()  #output 300
    

    但是上面例子中的类方法修改为实例方法也是可以的,并没有多大的影响

    同时我们可以看下面这个三种方法得对比:

    class A(object):
        def instance_method(self,x):
            print "calling instance method instance_method(%s,%s)"%(self,x)
        @classmethod
        def class_method(cls,x):
            print "calling class_method(%s,%s)"%(cls,x)
        @staticmethod
        def static_method(x):
            print "calling static_method(%s)"%x
    a = A()
    a.instance_method("test")
    #输出calling instance method instance_method(<__main__.A object at 0x00D66B50>,test)
    a.class_method("test")
    #输出calling class_method(<class '__main__.A'>,test)
    a.static_method("test")
    #输出calling static_method(test)
    

    调用实例方法的时候:

    [图片上传失败...(image-790e53-1582298210229)]

    调用类方法的时候

    [图片上传失败...(image-f2734b-1582298210229)]

    浅拷贝和深拷贝

    这一段代码,模拟了一个客户到披萨点点餐

    import copy
    # 披萨店
    class Pizza(object):
           def __init__(self,name,size,price):
                     self.name=name
                     self.size=size
                     self.price=price
           def getPizzaInfo(self):
                #①获取Pizza相关信息
                return self.name,self.size,self.price
           def showPizzaInfo(self):
                #②显示Pizza信息
                print "Pizza name:"+self.name
                print "Pizza size:"+str(self.size)
                print "Pizza price:"+str(self.price)
           def changeSize(self,size):
                self.size=size
           def changePrice(self,price):
                self.price=price
    
    # 具体的订单
    class Order(object):
            #③订单类
        def __init__(self,name):
            self.customername=name
            self.pizzaList=[]
            self.pizzaList.append(Pizza("Mushroom",12,30))
        def ordermore(self,pizza):
            self.pizzaList.append(pizza)
        def changeName(self,name):
            self.customername=name
        def getorderdetail(self):
            print "customer name:"+self.customername
            for i in self.pizzaList:
                i.showPizzaInfo()
        def getPizza(self,number):
                return self.pizzaList[number]
    
    customer1=Order("zhang") # 新客户 zhang
    customer1.ordermore(Pizza("seafood",9,40))
    customer1.ordermore(Pizza("fruit",12,35))
    print "customer1 order infomation:"
    customer1.getorderdetail()
    print "-------------------------------"
    

    此时又来了一个顾客,订单和客户1一样,只是要将预定的水果披萨的尺寸和价格进行相应的修改,于是为了方便直接拷贝了客户1的订单

    customer2=copy.copy(customer1)  # 注意此处是浅拷贝
    print "order 2 customer name:"+customer2.customername
    customer2.changeName("li")
    customer2.getPizza(2).changeSize(9)
    customer2.getPizza(2).changePrice(30)
    print "customer2 order infomation:"
    customer2.getorderdetail()
    print "-------------------------------------"
    

    客户2的订单完成了,但是你会发现客户1的订单也被改变了

    [图片上传失败...(image-ac75be-1582298210229)]

    浅拷贝并没有复制 pizza ,导致两份订单中的 pizza 指向同一块内存地址

    换成 copy.deepcopy 就没问题了

    python字符串

    多行字符串技巧

    >>> a = ('aaaa'
    ... 'bbbbb'
    ... 'ccccc'
    ... 'ddddd'
    ... 'eeeee')  # 遇到未闭合的小括号会将所有的字符串连在一起
    >>> a
    'aaaabbbbbcccccdddddeeeee'
    

    性质判定

    isalnum()、isalpha()、isdigit()、islower()、isupper()、isspace()、istitle()、startswith(prefix[,start[, end]])、endswith(suffix[,start[, end]])
    

    查找和替换

    count( sub[, start[, end]])、find( sub[, start[,end]])、index( sub[, start[, end]])、rfind( sub[, start[,end]])、rindex(sub[, start[, end]])
    

    注意find()和index()方法的不同:find()函数族找不到时返回-1,index()函数族则抛出ValueError异常。

    但是对于判定是否包含字串的判定推荐用 innot in操作符

    replace(old, new[,count])用以替换字符串的某些子串,如果指定count参数的话,就最多替换count次,如果不指定,就全部替换

    分切与连接

    partition()split

    split 举例:

    >>> ' hello     world'.split()
    ['hello', 'world']
    >>> ' hello     world'.split(' ')  # 要注意第一种方式和第二种方式不一样
    ['', 'hello', '', '', '', '', 'world']
    >>> ' hello     world'.split('')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ValueError: empty separator
        
    
    >>> ''.split()
    []
    >>> ''.split(' ')
    ['']
    

    变形

    lower()、upper()、capitalize()、swapcase()、title()

    删减与填充

    strip([chars])、lstrip([chars])、rstrip([chars])

    center(width[, fillchar])、ljust(width[,fillchar])、rjust(width[, fillchar])、zfill(width)、expandtabs([tabsize])

    这些方法中的fillchar参数是指用以填充的字符,默认是空格。而zfill()中的z是指zero,所以顾名思义,zfill()即是以字符0进行填充,在输出数值时比较常用。expandtabs()的tabsize参数默认为8,它的功能是把字符串中的制表符(tab)转换为适当数量的空格。

    异常处理

    基本语法:

    try:
    <语句>        #运行别的代码
    except <名字>:
    <语句>        #如果在try部份引发了'name'异常
    except <名字>,<数据>:
    <语句>        #如果引发了'name'异常,获得附加的数据
    else:
    <语句>        #如果没有异常发生
    

    如果 try 中发生了异常,那么根据发生的异常进入到 except 语句块中

    之后如果没有异常发生,那么 try 完成之后进入 else 语句块

    [图片上传失败...(image-8318d9-1582298210229)]

    相关文章

      网友评论

          本文标题:编写高质量代码阅读笔记

          本文链接:https://www.haomeiwen.com/subject/ophkqhtx.html