python进阶--解锁更多使用python的姿势

PEP8 python编程规范

想要代码水平有所提高,总免不了code review,这个环节除了考察逻辑漏洞,我觉得最关键的一点就是代码的可读性,一个可读性高的代码,更加易读易维护.PEP8包含了大量的如何编写python代码的细节.下面列举几个重要的规则:

空格

在python中,空格是一个很重要的语法,空格的普适性建议:

  • 使用空格而不是用tab键来缩进.
  • 使用4个空格作每一层的缩进
  • 每行代码应该尽量少于79个字符
  • 在一个文件中,函数和类之间用两个空行隔开
  • 方法与方法之间用一个空行隔开
  • 列表索引,函数调用或关键字参数赋值两侧不要用空格
  • 变量赋值的等号两边有且仅有一个空格

表达式和语句

  • 推荐(if a is not b),不推荐(if not a is b)
  • 推荐(if not somelist),不推荐(if len(somelist) == 0)
  • if,while,for, try,except等分支关键字请不要放在一行
  • 总是把import写在文件最顶部

*args和**kwargs

python的可变参数,比起java要讨喜的多,一个可变参数的方法,解决了java的n多方法重写,有这么好用的特性,不得安排起来吗?

使用场景是预先不知道调用者会传入多少参数的时候

注: *args 和 **kwargs 并不是唯一写法,如果你喜欢,可以写成 *vars , 以及 **vars

*args

*args用来接收一个有序的参数列表,在方法内部可以把args当做list来调用

比如,内置的sum只能求可迭代列表的和,我们想要自己定义一个求和方法:

def my_sum(*args):
    res = 0
    for n in args:
        res+=n
    return res

定义了上述方法,我们就可以用my_sum(1,4,5,6,7,8)来求得不确定个数的和了.

**kwargs

*kwargs用来接收一个参数键值对,在方法内部可以把kwargs当做dict来调用:

比如我们要写一个方法保存不确定的键值对到非关系型数据库.

def save_kv(**kwargs):
    for k,v in kwargs.items():
        save_single_record(k,v)

save_kv可以用两种方式来调用:

  • save_kv(name=‘zhangsan’,age=18)

  • params = {‘name’:‘zhangsan’,‘age’:18}

    save_kv(**params)

调试Debugging

不同于Java这种长在IDE上面的编程语言(不是说没有IDE就写不了Java,我是说大部分情况下写Java都在IDE中),python代码由于轻量,linux内置,很多时候没有IDE的debug环境,很多时候vim就在服务器上面写完了python,那如果出问题了如何debug呢?

答案是pdb(python debug),官网链接附上:

python2 debug

python3 debug

从命令行debug

$ python -m pdb short_script.py

这会触发debugger在脚本第一行指令处停止,在脚本很短的时候很有用;

从脚本内部运行

import pdb

def make_bread():
	pdb.set_trace()
	return "I don't have time"

print(make_bread())

pdb.set_trace()就是在这一行打了一个断点,与IDE里面的debug模式意思一样.

debugger模式怎么操作呢?

按键操作
c继续执行
w显示正在执行的代码的上下文信息
a打印当前函数的参数列表
s单步进入(IDE中的step into)
n单步跳过(IDE中的step over)

生成器 Generators 和迭代器 Iterators

可迭代对象(Iterable)

python中的任意对象,只要它定义了可以返回一个迭代器__iter__方法,或者定义了可以支持下标索引的__getitem__方法,那么它就是一个可迭代对象.

迭代器(Iterator)

任意对象,只要定义了next(python2),__next__(python3)方法,它就是一个迭代器.

生成器(Generators)

生成器也是一种迭代器,但是你只能对其迭代一次,因为它们并没有把所有的值保存在内存中,而是在运行时生成.大多数时候生成器是以函数来实现的.然而它们并不是返回一个值,而是用yield(“生出”)一个值.

当斐波那契数列传的n的值很大的时候,就可以用生成器来写:

def fib_gen(n):
    a=b=1
    for i in range(n):
        yield a
        a,b = b,a+b

它跟传统方式有什么区别?

传统return写法,如果n=100000(十万),这个方法调用需要占用大量的内存,因为它在内存中开辟了一个十万大小的list,然而用yield就完全不用担心.

三元运算符

同很多编程语言一样,python也有三元运算符.

python的三元运算符有两种写法:

human语言习惯法

为什么叫人类语言习惯法?看下面伪代码:

去打球 if 不下雨 else 宅家里

这,后置说法可能山东老哥们看起来倍儿熟悉吧.

machine的01法

0是False,1是True,数据库里面是不是很常见,这种思想同样可以被用到三元表达式中,对于上述伪代码,就变成了下面这样

(宅家里,去打球)[不下雨]

不下雨会被计算成0或1,0对应二元组的第一个也就是三元运算否的情况,1对应二元组第二个也就是三元运算是的情况.

装饰器 Decorator

相信学过spring的同学都听过AOP这么一个听起来很高大上的玩意,面向切面编程,用注解实现方法执行前后的代码,在python里面,装饰器就可以实现AOP.

要理解装饰器,首先要明确一个概念,方法也是对象,方法也可以当做方法参数

方法也是对象

方法也是对象怎么理解?为了方便理解这个重要概念,看下面代码

> def hi(name='zhangsan'):
>     return "hi," + name
> print(hi())
#output: 'hi,zhangsan'
#重点来了
> greet = hi
> print(greet())
#output: 'hi,zhangsan'
> del hi
> print(hi())
#NameError:Nosuch method
> print(greet())
#output: 'hi,zhangsan'

def似乎是定义了一个对象模板,第六行就像对象赋值一样把hi赋值给了greet,由此看来方法也是对象.

方法可以当做方法的参数

正是AOP的核心思想,在python中,方法可以接收一个方法当做参数在其内部执行.

def myWrapper(func):
    def wrapTheFunction():
        print('look! this is pythonic @Before')
        func()
        print('and this is pythonic @After')
    return wrapTheFunction

上面方法参数是一个方法,返回值也是一个方法,实现了AOP般的装饰Before,After是不是在Spring中见过?

对于一个其他方法来说,就可以用注解来装饰此方法了:

@myWrapper
def fun():
    print('this is a function')

自定义的myWrapper,实现了对方法的装饰,但是有一个问题,对于被装饰的方法,print(fun.__name__)打印出来的就是myWrapper,就是说我们把方法包装起来之后,只能看到包装了,那么怎么在包装起来的同时,不改变原来的name呢?

只要用functools.wraps来修改一下我们的装饰器即可:

from functools import wraps
def myWrapper(func):
    @wraps(func)
    def wrapTheFunction():
        print('look! this is pythonic @Before')
        func()
        print('and this is pythonic @After')
    return wrapTheFunction

装饰器的应用场景

授权

授权是web服务面向切面编程的一个典型使用场景,python中的Django跟flask也有大量的使用场景,这里是一个装饰器授权的demo

from functools import wraps

def require(f):
    @wraps(f)
    def decorated(*args,**kwargs)
    	auth = request.authorization
        if not auth:
            authenticate()
        return f(*args, **kwargs)
    return decorated
日志封装
from functools import wraps

def logit(f):
    @wraps(f)
    def with_logging(*args,**kwargs):
        print(f.__name__ + " was called ")
    	return f(*args,**kwargs)
   	return with_logging

@logit
def demo(x):
    """do something"""
    return y

# output: demo was called

是不是有点Spring boot的意思了,但是你可能会说,Spring Boot的注解是可以加参数的,python 也可以!

因为@wraps也是装饰器,它接收一个参数,就像普通函数那样,记住,方法就是对象,那么解决方案就是_再!包!一!层!_

from functools import wraps

#不传参的时候默认往std.out里面打印
def logit(logfile='std.out')
	def logging_decorator(f):
        @wraps(f)
       	def wrap_func(*args, **kwargs):
            log_str = f.__name__ + "was called"
            with open(logfile,'a') as file:
                file.write(log_str + '\n')
            return f(*args, **kwargs)
        return wrap_func
    return logging_decorator

@logit()
def myfunc1():
    """do something"""
    pass

#往std.out里面打印myfunc1 was called

@logit(logfile='demo.log')
def myfunc2():
    """dosome thing"""
    pass
#往demo.log里面打印myfun2 was called

__slots__魔法

在python中,每个类都有实例属性,默认情况下python会用一个字典来保存这些变量的属性名跟属性值.

这通常很有用,它允许我们子啊运行时任意设置新的属性.

然而,如果这个类拥有很多已知属性的小类来说,由于字典的可扩展性我们可以猜到,为了保证在扩展时有内存可用,通常会"预留"较大的内存,如果小类的成员数量已知.我们怎么告诉python不要使用可扩展的字典,而是使用一种固定大小的数据结构呢?

答案是__slot__

不使用__slot__的传统方式

class MyClass(object):
    def __init__(self,name,age)
    	self.name = name
        self.age = age
        self.set_up()

使用了__slot__:

class MyClass(object):
    __slot__ = ['name','age']
    self.name = name
    self.age = age
    self.set_up()

第二段代码会为内存减轻负担,通过这个技巧,有些人已经看到内存占用率几乎减少了40%-50%

集合框架 Collections

尽管python的字典元组列表等数据结构已经足够灵活,但是面对一些复杂的结构化数据处理时,还是有些费力,这个时候,就要用到内置的集合框架包collections了.

defaultdict

defaultdict与dict不同,我们不需要检查key是否存在,比如

from collections import defaultdict

colours = (
    ('Yasoob','Yellow'),
    ('Ali','Blue'),
    ('Arham','Green'),
    ('Ali','Black'),
    ('Yasoob','Red'),
    ('Ahmed','Silver'),
)

favorite_colours = defaultdict(list)

for name,color in colours:
    favorite_colours[name].append(color)

print(favorite_colours)
#output
# defaultdict(<type 'list'>,
#{'Arham': ['Green'],
# 'Yasoob': ['Yellow', 'Red'],
# 'Ahmed': ['Silver'],
# 'Ali': ['Blue', 'Black']
# })

或者,可以直接嵌套赋值:

import collections
tree = lambda: collections.defaultdict(tree)
some_dict = tree()
some_dict['color']['favorite'] = 'blue'

# {"color":{'favorite':'blue'}}

Counter

Counter是一个计数器,可以针对某项数据进行计数

from collections import Counter

colors = ['blue','green','yellow','blue','blue','yellow']
cnts = Counter(colors)
print(cnts)
# {'blue':3,'yellow':2,'green':1}

deque

deque是一个限制了list部分方法的双端队列,可以从头尾两端添加或删除元素,而且在创建时指定maxlen,就可以限制大小.如果超过大小,数据会从队列另一段被挤出(pop)

推导式 Comprehension

推倒式在python 2和3中都有支持,是python独有且好用的特性,有了它,甚至不用知道map,filter,因为在python3里面 map的结果是一个map对象,filter的结果是一个filter对象,这些用推导式就可以搞定.

列表推导式

nums = [1,2,3,4,5,6]

#map推导式,对nums中的每个元素平方
nums2 = [i**2 for i in nums]
# 如果用map,需要这么写 num2 = list(map(lambda x:x**2, nums))

# filter,筛选偶数
num3 = [i for i in nums if i%2==0]
##如果用filter,需要写 num3 = list(filter(lambda x: x%2==0,nums) )

字典推导式

mcase = {'a':20,'b':15}
# kv互换
mcase_reverse = {v:k for k,v in mcase.items()}

集合推导式

集合推导式跟列表推导式相同,只是用的是大括号{},比如:

nums = [1,2,3,4,5,6]
nums2 = {x**2 for x in nums}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值