请选择 进入手机版 | 继续访问电脑版
 找回密码
 立即注册

QQ登录

只需要一步,快速开始

搜索
开启左侧

Python 有什么奇技淫巧?

马上注册,分享更多源码,享用更多功能,让你轻松玩转云大陆。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
0.这个问题虽说是找寻“奇技淫巧”,但其实是想抛砖引玉 1.如果想把自己认为或好玩或强大的python使用技巧拿出来跟大家分享,可否稍微详细的讲解一下
回复

使用道具 举报

大神点评20

很早之前整理过的
链式比较操作
>>> x = 5
>>> 1 < x < 10
True
>>> 10 < x < 20
False
>>> x < 10 < x*10 < 100
True
>>> 10 > x <= 9
True
>>> 5 == x > 4
True
你可能认为它执行的过程先是:1 < x,返回True,然后再比较True < 10,当然这么做也是返回True,比较表达式True < 10,因为解释器会把True转换成1,False转换成0。但这里的链式比较解释器在内部并不是这样干的,它会把这种链式的比较操作转换成:1 < x and x < 10,不信你可以看看最后一个例子。这样的链式操作本可以值得所有编程语言拥有,但是很遗憾,其他语言很少有这样的支持。  
enumerate

>>> a = ['a', 'b', 'c', 'd', 'e']
>>> for index, item in enumerate(a): print index, item
...
0 a
1 b
2 c
3 d
4 e
>>>
用enumerate包装一个可迭代对象,可以同时使用迭代项和索引,在迭代的同时获取迭代项所在位置时非常方便。如果你不这么干的话,下面有一种比较麻烦的方法:  
for i in range(len(a)):
    print i, a
enumerate 还可以接收一个可选参数start,默认start等于0。enumerate(list, start=1),这样index的起始值就是1  


生成器对象

x=(n for n in foo if bar(n))  #foo是可迭代对象
>>> type(x)
<type 'generator'>
你可以把生成器对象赋值给x,意味着可以对x进行迭代操作:  
for n in x:
    pass
它的好处就是不需要存储中间结果,也许你会使用(列表推导式):  
x = [n for n in foo if bar(n)]
>>> type(x)
<type 'list'>
它比生成器对象能带来更快的速度。相对地,生成器更能节省内存开销,它的值是按需生成,不需要像列表推倒式一样把整个结果保存在内存中,同时它不能重新迭代,列表推导式则不然。  
字典推导式

在Python2.6以下版本字典生成器可以接受迭代的键值对:
d = dict((key, value) for (key, value) in iterable)
从Python2.7或者Python3以后,你可以直接用字典推导式语法:
d = {key: value for (key, value) in iterable}
你也可以用任何方式的迭代器(元组,列表,生成器..),只要可迭代对象的元素中有两个值,
d = {value: foo(value) for value in sequence if bar(value)}
更为高级的用法:
def key_value_gen(k):
   yield chr(k+65)
   yield chr((k+13)%26+65)
d = dict(map(key_value_gen, range(26)))
iter()可接收callable参数

iter()内建函数接收的参数分为两种,第一种是:  
iter(collection)---> iterator
参数collection必须是可迭代对象或者是序列 ,第二种是:  
iter(callable, sentinel) --> iterator
callable函数会一直被调用,直到它的返回结果等于sentinel,例如:  
def seek_next_line(f):
    #每次读一个字符,直到出现换行符就返回
    for c in iter(lambda: f.read(1),'\n'):  
        pass
可变的默认参数

>>> def foo(x=[]):
...     x.append(1)
...     print x
...
>>> foo()
[1]
>>> foo()
[1, 1]
>>> foo()
[1, 1, 1]


取而代之的是你应该使用一个标记值表示“没有指定”来替换可变对象,如:  
>>> def foo(x=None):
...     if x is None:
...         x = []
...     x.append(1)
...     print x
>>> foo()
[1]
>>> foo()
[1]
发送值到生成器函数在中

def mygen():
    """Yield 5 until something else is passed back via send()"""
    a = 5
    while True:
        f = (yield a) #yield a and possibly get f in return
        if f is not None:
            a = f  #store the new value
你可以:  
>>> g = mygen()
>>> g.next()
5
>>> g.next()
5
>>> g.send(7)  #we send this back to the generator
7
>>> g.next() #now it will yield 7 until we send something else
7
如果你不喜欢使用空格缩进,那么可以使用C语言花括号{}定义函数:

>>> from __future__ import braces   #这里的braces 指的是:curly braces(花括号)
  File "<stdin>", line 1
SyntaxError: not a chance
当然这仅仅是一个玩笑,想用花括号定义函数?没门。感兴趣的还可以了解下:  
from __future__ import barry_as_FLUFL
不过这是python3里面的特性,http://www.python.org/dev/peps/pep-0401/  


切片操作中的步长参数

a = [1,2,3,4,5]
>>> a[::2]  # iterate over the whole list in 2-increments
[1,3,5]
还有一个特例:x[::-1],反转列表:  
>>> a[::-1]
[5,4,3,2,1]
有关反转,还有两个函数reverse、reversed,reverse是list对象的方法,没有返回值,而reversed是内建方法,可接收的参数包括tuple、string、list、unicode,以及用户自定义的类型,返回一个迭代器。  
>>> l = range(5)
>>> l
[0, 1, 2, 3, 4]
>>> l.reverse()
>>> l
[4, 3, 2, 1, 0]
>>> l2 = reversed(l)
>>> l2
<listreverseiterator object at 0x99faeec>
装饰器

装饰器使一个函数或方法包装在另一个函数里头,可以在被包装的函数添加一些额外的功能,比如日志,还可以对参数、返回结果进行修改。装饰器有点类似Java中的AOP。下面这个例子是打印被装饰的函数里面的参数的装饰器,  
>>> def print_args(function):
>>>     def wrapper(*args, **kwargs):
>>>         print 'Arguments:', args, kwargs
>>>         return function(*args, **kwargs)
>>>     return wrapper

>>> @print_args
>>> def write(text):
>>>     print text

>>> write('foo')
Arguments: ('foo',) {}
foo
@是语法糖,它等价于:  
>>> write = print_args(write)
>>> write('foo')
arguments: ('foo',) {}
foo


for ... else语法

for i in foo:
    if i == 0:
        break
else:
    print("i was never 0")
else代码块只有在for循环正常结束后执行如果遇到break语句那么不会执行else语句块,等价于下面:  
found = False
for i in foo:
    if i == 0:
        found = True
        break
if not found:
    print("i was never 0")
不过这种语法看起来怪怪地,让人感觉是else块是在for语句块没有执行的时候执行的,很容易让人去类比 if else 的语法,如果是把else换成finally或许更容易理解   


python2.5中的__missing__方法

dict的子类如果定义了方法__missing__(self, key),如果key不再dict中,那么d[key]就会调用__missing__方法,而且d[key]的返回值就是__missing__的返回值。  


>>> class MyDict(dict):
...  def __missing__(self, key):
...   self[key] = rv = []
...   return rv
...
>>> m = MyDict()
>>> m["foo"].append(1)
>>> m["foo"].append(2)
>>> dict(m)
{'foo': [1, 2]}


在collections模块下有一个叫defaultdict的dict子类,它与missing非常类似,但是对于不存在的项不需要传递参数。  
>>> from collections import defaultdict
>>> m = defaultdict(list)
>>> m["foo"].append(1)
>>> m["foo"].append(2)
>>> dict(m)
{'foo': [1, 2]}


变量值交换

>>> a = 10
>>> b = 5
>>> a, b
(10, 5)

>>> a, b = b, a
>>> a, b
(5, 10)
等号右边是一个创建元组的表达式,等号左边解压(没有引用的)元组分别赋给名称(变量)a和b。赋完值后因为没有被其他名字引用,因此被标记之后被垃圾收集器回收,而绑定到a和b的值已经被交换了。

注意:多值赋值其实仅仅就是元组打包和序列解包的组合的过程  


可读的正则表达式

在Python中你可以把正则表达式分割成多行写,还可以写注释  
>>> pattern = """
... ^                   # beginning of string
... M{0,4}              # thousands - 0 to 4 M's
... (CM|CD|D?C{0,3})    # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's),
...                     #            or 500-800 (D, followed by 0 to 3 C's)
... (XC|XL|L?X{0,3})    # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X's),
...                     #        or 50-80 (L, followed by 0 to 3 X's)
... (IX|IV|V?I{0,3})    # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's),
...                     #        or 5-8 (V, followed by 0 to 3 I's)
... $                   # end of string
... """
>>> re.search(pattern, 'M', re.VERBOSE)


函数参数解包(unpacking)

分别使用*和**解包列表和字典,这是一种非常实用的快捷方式,因为list,tuple,dict作为容器被广泛使用   
def draw_point(x, y):
    # do some magic

point_foo = (3, 4)
point_bar = {'y': 3, 'x': 2}

draw_point(*point_foo)
draw_point(**point_bar)
动态地创建新类型

动态创建新类型虽不是实用功能,但了解一下也是有好处的  


>>> NewType = type("NewType", (object,), {"x": "hello"})
>>> n = NewType()
>>> n.x
"hello"
type的第一个参数就是类名,第二个参数是继承的父类,第三个参数是类的属性.它完全等同于:  
>>> class NewType(object):
>>>     x = "hello"
>>> n = NewType()
>>> n.x
"hello"


上下文管理器与with语句

上下文管理器(context manager)用于规定某个对象的使用范围,进入或退出该范围时,特殊的操作会被执行(比如关闭连接,释放内存等等),语法是:with... as ...,该特性在python2.5引入的.
上下文管理器协议有两个方法组成contextmanager.__enter__()和contextmanager.__exit__(),任何实现了这两个方法的对象都称之为上下文管理器对象,比如文件对象就默认实现了该协议.


with open('foo.txt', 'w') as f:
    f.write('hello!')


字典的get()方法

字典的get()方法用来替换d['key'],后者如果是遇到key不存在会有异常,如果使用的d.get('key'),key不存在时它返回的是None,你可以指定两个参数如:d.get('key',0)来用0取代返回的None  
sum[value] = sum.get(value, 0) + 1
还有一个类似的方法setdefault(key, value),如果字典中存在key,那么就直接返回d[key],否则设置d[key]=value,并返回该值.  
>>> d = {'key':123}
>>> d.setdefault('key',456)
123
>>> d['key']
123
>>> d.setdefault('key2',456)
456
>>> d['key2']
456
collections.Counter是dict的子类,用来统计可哈稀对象,
>>> cnt = Counter('helloworld')
>>> cnt
Counter({'l': 3, 'o': 2, 'e': 1, 'd': 1, 'h': 1, 'r': 1, 'w': 1})
>>> cnt['l']
3
>>> cnt['x'] = 10
>>> cnt.get('y')


描述符(Descriptors)

描述符是python的核心特新之一,当你使用.访问成员时,(如:x.y),python首先在实例字典中查找该成员,如果没有发现再从类字典中查找,如果这个对象实现了描述符(实现了__get__,__set__,__delete__),那么优先返回__get__方法的返回值.  


条件赋值

为什么python中没有类c语言的三目运算符,Guido van Rossum说过了,条件赋值更容易理解  
x = 3 if (y == 1) else 2
这个表达式的意思就是:如果y等于那么就把3赋值给x,否则把2赋值给x, 条件中的括号是可选的,为了可读性可以考虑加上去.if else中的表达式可以是任何类型的,既可以函数,还可以类  
(func1 if y == 1 else func2)(arg1, arg2)
如果y等于1,那么调用func1(arg1,arg2)否则调用func2(arg1,arg2)  
x = (class1 if y == 1 else class2)(arg1, arg2)
class1,class2是两个类  


异常else语句块

try:
   try_this(whatever)
except SomeException, exception:
   #Handle exception
else:
    # do something
finally:
    #do something
else语句块会在没有异常的情况下执行,先于finally,它的好处就是你可以明确知道它会在没有异常的情况下执行,如果是把else语句块放在try语句块里面就达不到这种效果.
回复

使用道具 举报

风来时狂放 2019-11-6 14:25:36 显示全部楼层
谈到奇技淫巧,我认为这款神器当之无愧。
在程序开发过程中,代码的运行往往会和我们预期的结果有所差别。于是,我们需要清楚代码运行过程中到底发生了什么?代码哪些模块运行了,哪些模块没有运行?输出的局部变量是什么样的?很多时候,我们会想到选择使用成熟、常用的IDE使用断点和watches调试程序,或者更为基础的print函数、log打印出局部变量来查看是否符合我们预期的执行效果。但是这些方法都有一个共同的弱点--效率低且繁琐,本文就介绍一个堪称神器的Python调试工具PySnooper,能够大大减少调试过程中的工作量。
装饰器

装饰器(Decorators)是Python里一个很重要的概念,它能够使得Python代码更加简洁,用一句话概括:装饰器是修改其他函数功能的函数。PySnooper的调用主要依靠装饰器的方式,所以,了解装饰器的基本概念和使用方法更有助于理解PySnooper的使用。在这里,我先简单介绍一下装饰器的使用,如果精力有限,了解装饰器的调用方式即可。
对于Python,一切都是对象,一个函数可以作为一个对象在不同模块之间进行传递,举个例子,
def one(func):
    print("now you are in function one.")
    func()

   
def two():
    print("now you are in function two")

   
one(two)

# 输出
>>> now you are in function one.
>>> now you are in function two.其实这就是装饰器的核心所在,它们封装一个函数,可以用这样或那样的方式来修改它。换一种方式表达上述调用,可以用@+函数名来装饰一个函数。
def one(func):
    print("now you are in function one.")
    def warp():
        func()
    return warp


@one
def two():
    print("now you are in function two.")

   
two()

# 输出
>>> now you are in function one.
>>> now you are in function two.此外,在调用装饰器时还可以给函数传入参数:
def one(func):
    print("now you are in function one.")
    def warp(*args):
        func(*args)
    return warp


@one
def two(x, y):
    print("now you are in function two.")
    print("x value is %d, y value is %d" % (x, y))

   
two(5, 6)

# 输出
>>> now you are in function one.
>>> now you are in function two.
>>> x value is 5, y value is 6另外,装饰器本身也可以接收参数,
def three(text):
    def one(func):
        print("now you are in function one.")
        def warp(*args):
            func(*args)
        return warp
    print("input params is {}".format(text))
    return one


@three(text=5)
def two(x, y):
    print("now you are in function two.")
    print("x value is %d, y value is %d" % (x, y))

   
two(5, 6)

# 输出
>>> input params is 5
>>> now you are in function one.
>>> now you are in function two.
>>> x value is 5, y value is 6上面讲述的就是Python装饰器的一些常用方法。
Pysnooper

调试程序对于大多数开发者来说是一项必不可少的工作,当我们想要知道代码是否按照预期的效果在执行时,我们会想到去输出一下局部变量与预期的进行比对。目前大多数采用的方法主要有以下几种:
    Print函数Log日志IDE调试器
但是这些方法有着无法忽视的弱点:
    繁琐过度依赖工具
"PySnooper is a poor man's debugger."
有了PySnooper,上述问题都迎刃而解,因为PySnooper实在太简洁了,目前在github已经10k+star。
前面花了一段篇幅讲解装饰器其实就是为了PySnooper做铺垫,PySnooper的调用就是通过装饰器的方式进行使用,非常简洁。
PySnooper的调用方式就是通过@pysnooper.snoop的方式进行使用,该装饰器可以传入一些参数来实现一些目的,具体如下:
参数描述None输出日志到控制台filePath输出到日志文件,例如'log/file.log'prefix给调试的行加前缀,便于识别watch查看一些非局部变量表达式的值watch_explode展开值用以查看列表/字典的所有属性或项depth显示函数调用的函数的snoop行
安装
使用pip安装,
pip install pysnooper 或者使用conda安装,
conda install -c conda-forge pysnooper使用
先写一个简单的例子,
import numpy as np
import pysnooper


@pysnooper.snoop()
def one(number):
    mat = []
    while number:
        mat.append(np.random.normal(0, 1))
        number -= 1
    return mat


one(3)

# 输出

Starting var:.. number = 3
22:17:10.634566 call         6 def one(number):
22:17:10.634566 line         7     mat = []
New var:....... mat = []
22:17:10.634566 line         8     while number:
22:17:10.634566 line         9         mat.append(np.random.normal(0, 1))
Modified var:.. mat = [-0.4142847169210746]
22:17:10.634566 line        10         number -= 1
Modified var:.. number = 2
22:17:10.634566 line         8     while number:
22:17:10.634566 line         9         mat.append(np.random.normal(0, 1))
Modified var:.. mat = [-0.4142847169210746, -0.479901983375219]
22:17:10.634566 line        10         number -= 1
Modified var:.. number = 1
22:17:10.634566 line         8     while number:
22:17:10.634566 line         9         mat.append(np.random.normal(0, 1))
Modified var:.. mat = [-0.4142847169210746, -0.479901983375219, 1.0491540468063252]
22:17:10.634566 line        10         number -= 1
Modified var:.. number = 0
22:17:10.634566 line         8     while number:
22:17:10.634566 line        11     return mat
22:17:10.634566 return      11     return mat
Return value:.. [-0.4142847169210746, -0.479901983375219, 1.0491540468063252]这是最简单的使用方法,从上述输出结果可以看出,PySnooper输出信息主要包括以下几个部分:
    局部变量值代码片段局部变量所在行号返回结果
也就是说,把我们日常DEBUG所需要的主要信息都输出出来了。
上述结果输出到控制台,还可以把日志输出到文件,
@pysnooper.snoop("debug.log") Python 有什么奇技淫巧?-1.jpg 在函数调用过程中,PySnooper还能够显示函数的层次关系,使得一目了然,
@pysnooper.snoop()
def two(x, y):
    z = x + y
    return z


@pysnooper.snoop()
def one(number):
    k = 0
    while number:
        k = two(k, number)
        number -= 1
    return number


one(3)

# 输出
Starting var:.. number = 3
22:26:08.185204 call        12 def one(number):
22:26:08.186202 line        13     k = 0
New var:....... k = 0
22:26:08.186202 line        14     while number:
22:26:08.186202 line        15         k = two(k, number)
    Starting var:.. x = 0
    Starting var:.. y = 3
    22:26:08.186202 call         6 def two(x, y):
    22:26:08.186202 line         7     z = x + y
    New var:....... z = 3
    22:26:08.186202 line         8     return z
    22:26:08.186202 return       8     return z
    Return value:.. 3
Modified var:.. k = 3
22:26:08.186202 line        16         number -= 1
Modified var:.. number = 2
22:26:08.186202 line        14     while number:
22:26:08.186202 line        15         k = two(k, number)
    Starting var:.. x = 3
    Starting var:.. y = 2
    22:26:08.186202 call         6 def two(x, y):
    22:26:08.186202 line         7     z = x + y
    New var:....... z = 5
    22:26:08.186202 line         8     return z
    22:26:08.186202 return       8     return z
    Return value:.. 5
Modified var:.. k = 5
22:26:08.186202 line        16         number -= 1
Modified var:.. number = 1
22:26:08.186202 line        14     while number:
22:26:08.186202 line        15         k = two(k, number)
    Starting var:.. x = 5
    Starting var:.. y = 1
    22:26:08.186202 call         6 def two(x, y):
    22:26:08.186202 line         7     z = x + y
    New var:....... z = 6
    22:26:08.186202 line         8     return z
    22:26:08.186202 return       8     return z
    Return value:.. 6
Modified var:.. k = 6
22:26:08.186202 line        16         number -= 1
Modified var:.. number = 0
22:26:08.186202 line        14     while number:
22:26:08.186202 line        17     return number
22:26:08.186202 return      17     return number
Return value:.. 0从上述输出结果可以看出,函数one与函数two的输出缩进层次一目了然。
除了缩进之外,PySnooper还提供了参数prefix给debug信息添加前缀的方式便于识别,
@pysnooper.snoop(prefix="funcTwo ")
def two(x, y):
    z = x + y
    return z


@pysnooper.snoop(prefix="funcOne ")
def one(number):
    k = 0
    while number:
        k = two(k, number)
        number -= 1
    return number


one(3)

# 输出
funcOne Starting var:.. number = 3
funcOne 22:28:14.259212 call        12 def one(number):
funcOne 22:28:14.260211 line        13     k = 0
funcOne New var:....... k = 0
funcOne 22:28:14.260211 line        14     while number:
funcOne 22:28:14.260211 line        15         k = two(k, number)
funcTwo     Starting var:.. x = 0
funcTwo     Starting var:.. y = 3
funcTwo     22:28:14.260211 call         6 def two(x, y):
funcTwo     22:28:14.260211 line         7     z = x + y
funcTwo     New var:....... z = 3
funcTwo     22:28:14.260211 line         8     return z
funcTwo     22:28:14.260211 return       8     return z
funcTwo     Return value:.. 3
funcOne Modified var:.. k = 3
funcOne 22:28:14.260211 line        16         number -= 1
funcOne Modified var:.. number = 2
funcOne 22:28:14.260211 line        14     while number:
funcOne 22:28:14.260211 line        15         k = two(k, number)
funcTwo     Starting var:.. x = 3
funcTwo     Starting var:.. y = 2
funcTwo     22:28:14.260211 call         6 def two(x, y):
funcTwo     22:28:14.260211 line         7     z = x + y
funcTwo     New var:....... z = 5
funcTwo     22:28:14.260211 line         8     return z
funcTwo     22:28:14.260211 return       8     return z
funcTwo     Return value:.. 5
funcOne Modified var:.. k = 5
funcOne 22:28:14.260211 line        16         number -= 1
funcOne Modified var:.. number = 1
funcOne 22:28:14.260211 line        14     while number:
funcOne 22:28:14.260211 line        15         k = two(k, number)
funcTwo     Starting var:.. x = 5
funcTwo     Starting var:.. y = 1
funcTwo     22:28:14.260211 call         6 def two(x, y):
funcTwo     22:28:14.260211 line         7     z = x + y
funcTwo     New var:....... z = 6
funcTwo     22:28:14.260211 line         8     return z
funcTwo     22:28:14.260211 return       8     return z
funcTwo     Return value:.. 6
funcOne Modified var:.. k = 6
funcOne 22:28:14.260211 line        16         number -= 1
funcOne Modified var:.. number = 0
funcOne 22:28:14.260211 line        14     while number:
funcOne 22:28:14.260211 line        17     return number
funcOne 22:28:14.260211 return      17     return number
funcOne Return value:.. 0参数watch可以用于查看一些非局部变量,例如,
class Test():
    t = 10


test = Test()


@pysnooper.snoop(watch=("test.t", "x"))

# 输出
Starting var:.. number = 3
Starting var:.. test.t = 10
Starting var:.. x = 10参数watch_explode可以展开字典或者列表显示它的所有属性值,对比一下它和watch的区别,
#### watch_explode ####
d = {
    "one": 1,
    "two": 1
}


@pysnooper.snoop(watch_explode="d")

# 输出
Starting var:.. number = 3
Starting var:.. d = {'one': 1, 'two': 1}
Starting var:.. d['one'] = 1
Starting var:.. d['two'] = 1

#### watch ####
d = {
    "one": 1,
    "two": 1
}


@pysnooper.snoop(watch="d")

# 输出
Starting var:.. d = {'one': 1, 'two': 1}可以看出watch_explode能够展开字典的属性值。
另外还有参数depth显示函数中调用函数的snoop行,默认值为1,参数值需要大于或等于1。
福利

Python 有什么奇技淫巧?-2.jpg
我把《Python Cookbook》中文版和英文版、《Python编程:从入门到实践》、《Python编程快速上手》、《像计算机科学家一样思考Python (第2版)》、《流畅的Python》打包共享了,需要的可以关注公众号【平凡而诗意】,回复关键字"py"获取。
更多我的作品

Jackpop:请问自学Python有必要买课程吗?
Jackpop:Python调试工具之pdb
Jackpop:一文教你配置得心应手的Python
后续分享会以公众号和个人主页为主,感兴趣的请关注公众号【平凡而诗意】,或者收藏个人主页~
平凡而诗意
回复

使用道具 举报

实验楼python高级工程师@protream总结了一些他在工作中用到过,个人认为比较 Pythonic 的一些小技巧:
cached_property

它的作用是将一个方法的计算结果缓存到对象的 __dict__ 当中,熟悉 Flask 的人对这个应该不陌生,Django 应该也有类似的实现。这是 werkzeug 中的源码实现:
class cached_property(property):

  def __init__(self, func, name=None, doc=None):
        self.__name__ = name or func.__name__
        self.__module__ = func.__module__
        self.__doc__ = doc or func.__doc__
        self.func = func

    def __set__(self, obj, value):
        obj.__dict__[self.__name__] = value

    def __get__(self, obj, type=None):
        if obj is None:
            return self
        value = obj.__dict__.get(self.__name__, _missing)
        if value is _missing:
            value = self.func(obj)
            obj.__dict__[self.__name__] = value
        return value
曾经主要运用这个将公司网站的一个页面性能提升了 3~4 倍。
Flask 自己实现了一个线程安全的版本,locked_cached_property:
class locked_cached_property(object):
      def __init__(self, func, name=None, doc=None):
            self.__name__ = name or func.__name__
            self.__module__ = func.__module__
            self.__doc__ = doc or func.__doc__
            self.func = func
            self.lock = RLock()

    def __get__(self, obj, type=None):
        if obj is None:
            return self
        with self.lock:
            value = obj.__dict__.get(self.__name__, _missing)
            if value is _missing:
                value = self.func(obj)
                obj.__dict__[self.__name__] = value
            return value
类方法装饰器

学习 Python 一段时间应该都知道装饰器是什么了,也应该知道怎么写一个简单的装饰器了。但是如果想装饰一个类方法,怎么去访问到当前的对象或者类?在普通装饰器中是不太好做到的,这时候就需要借助描述器了。上面的 cached_property 的实现就是一个示范。用 cached_property装饰一个类方法后,当该方法被以属性的方式调用后,对象和类会被分别传递到 obj 和 type 参数中。
基于这个特性可以实现一个和 @property 对应的类属性装饰器 @classproperty
class classproperty(object):

     def __init__(self, func):
         self.func = classmethod(func)

     def __get__(self, obj=None, type=None):
         return self.func.__get__(obj, type)()
使用它可以将一个 classmethod 以属性的方式访问。
Python 单列模式

在网上搜索 Python 单例模式,能找到很多中实现,比如这样的:
class Singleton(object):

    def __init__(self):
        pass

    @classmethod
    def instance(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance
或者这样的:
def Singleton(cls):
    _instance = {}

    def _singleton(*args, **kargs):
        if cls not in _instance:
            _instance[cls] = cls(*args, **kargs)
        return _instance[cls]

    return _singleton


@Singleton
class A(object):
    a = 1

    def __init__(self, x=0):
        self.x = x
私以为如果真正理解 Python 的话,下面的才是 Pythonic 的做法:
class Singleton(object):
    def foo(self):
        pass
singleton = Singleton()
将上面的代码在 a.py 中,那么在中用的地方导入的 singleton 就是单例的。
from a import singleton
将一个对象转化成字典

如果一个类定义了 __getitem__ 和 keys 那么它的对象就能用 dict 函数转化成字典:
>>> class Person:
...     def __init__(self, name, age):
...         self.name = name
...         self.age = age
...     def __getitem__(self, key):
...         return getattr(self, key)
...     def keys(self):
...         return ('name', 'age')
...

>>> p = Person('Jike', 20)
>>> dict(p)
{'age': 20, 'name': 'Jike'}
这个特性有什么用处呢?举个例子,在些 Flask WEB 应用时,接口需要返回 jSON 序列化的分页数据,我们可以改造 flask-sqlalchemy 的 Pagination 对象:
from flask_sqlalchemy import Pagination as _Pagination

class Pagination(_Pagination):
    def __getitem__(self, key):
        return getattr(self, key)

      def items(self):
        def keys(self):
            return (
                'page', 'pages', 'total', 'items',
                'has_prev', 'next_num', 'has_next'
            )
这样分页对象就能很方便的转换成字典了。
作者:实验楼工程师 郑楠
GitHub: protream (Zheng Nan)
Python学习:Python_精选项目课程
回复

使用道具 举报

123456811 2019-11-6 16:04:57 显示全部楼层
一个小技巧,将一个 list 中的多个 list 合并为一个 list。有点绕,其实就是将类似如下形式的 list:
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
转化为:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
很多人第一时间肯定会想到用迭代器
[j for i in l for j in i]
但是,我猜应该有很多人不知道,Python 的内建函数 sum 同样可以用来干这样一件事:
sum(l, [])
sum 有两个参数,第二个参数可以用来指定一个初始值:
sum(sequence[, start]) -> value

Return the sum of a sequence of numbers (NOT strings) plus the value
of parameter 'start' (which defaults to 0).  When the sequence is
empty, return start.
这里的原理其实是:
[] + [1, 2, 3] + [4, 5, 6] + [7, 8, 9]
比较了一下用迭代器和sum函数所花的时间,发现 sum 略快:
Python 有什么奇技淫巧?-1.jpg
回复

使用道具 举报

万胜 2019-11-6 16:28:59 显示全部楼层
刚好看到个
Python 有什么奇技淫巧?-1.jpg
Python 有什么奇技淫巧?-2.jpg
来自于 python高级编程 作者是 @董伟明 说明在分享一个准备给公司讲python高级编程的slide 这里还有对应的视频讲解

====================================================================

另外一个感觉就是这个库了ajalt/fuckitpy · GitHub 中间的实现挺厉害的,访问源码逐行加入try: finally
回复

使用道具 举报

David902 2019-11-6 16:49:27 显示全部楼层
奇技淫巧总是散布于民间,比如这个号称“穷人的 Debugger”的 PySnooper,暖手程度简直逆天了。来一段代码:
from typing import List
import pysnooper

@pysnooper.snoop()
def do(n: int) -> List[int]:
    l: List[int] = []
    for item in range(n):
        l.append(item * 10)
    return l

do(5)简简单单却打印出了循环体里的变量情况:
Python 有什么奇技淫巧?-1.jpg 从此 Python 开发调试告别 print(...) 不是梦!
Python 为什么招人喜欢?因为它的工具总是最好的!总有很多的“神来之笔”。
回复

使用道具 举报

却写杂布计 2019-11-6 17:10:19 显示全部楼层
这组演示稿里提到的东西或许符合题主胃口:Don't do this - Richard Jones - PyCon AU 2013
回复

使用道具 举报

贺老师 2019-11-6 17:34:37 显示全部楼层
给大家推荐一个小技巧,一行的构造器:

#避免类初始化时大量重复的赋值语句
class A(object):
    def __init__(self, a, b, c, d, e, f):
        self.__dict__.update({k: v for k, v in locals().items() if k != 'self'})
回复

使用道具 举报

def add(x):
    class AddNum(int):
        def __call__(self, x):
            return AddNum(self.numerator + x)
    return AddNum(x)

print add(2)(3)(5)
# 10
print add(2)(3)(4)(5)(6)(7)
# 27
回复

使用道具 举报

123456790 2019-11-6 20:26:03 显示全部楼层
从 Python 2.3 开始,sys 包有一个属性,叫 meta_path 。可以通过给 sys.meta_path 注册一个 finder 对象,改变 import 的行为。甚至可以实现这样的功能:通过 import 来导入一个 json 文件中的数据。

举个例子,有一个 tester.json 文件,里面的内容是:
{
    "hello": "world",
    "this": {
        "can": {
            "be": "nested"
        }
    }
}
通过改变 sys.meta_path ,注册一个读取 json 数据的 finder 之后,就可以这样用:
>>> import tester
>>> tester
<module 'tester' from 'tester.json'>
>>> tester.hello
u'world'
>>> tester.this.can.be
u'nested'

那么这个 finder 如何实现呢?
看这里: kragniz/json-sempai: Use JSON files as if t...
回复

使用道具 举报

心随674 2019-11-6 20:57:30 显示全部楼层
与其说是奇巧淫技,不如说是python给我们提供的某种便利性,简简单单就实现了一些比较复杂有用的功能。
我以循环迭代的常用技巧为例,即range、zip和enumerate的使用实例,来展示一下python的便利性。正是许多这些细小的功能点,组成了所谓的奇巧淫技。
内置函数range:返回一系列连续增加的整数
这个函数产生的连续增加的整数序列,可以作为for循环迭代的索引
for x in range(5):
print(x, end=',')

0,1,2,3,4,range也可以用在任何需要整数列表的地方。直接打印range函数的返回值是不能直接返回一个整数列表的,如果将其作为一个参数传给list函数,则可以一次性显示全部结果。
print(range(5))

range(0, 5)

print(list(range(-5,5)))

[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]内置函数range在for循环中是最常用的,它提供了一种简单的方法,重复特定次数的动作。
当然还可以做一些定制工作,比如在遍历的过程中跳过一些元素。例如每隔一个元素对字符串进行遍历:
S = 'abcdefghijklmn'
for i in range(0,len(S),2):
print(S, end=',')

a,c,e,g,i,k,m,当然,这只是一个示例,我们之前提到过的分片表达式才是实现该功能的最简单的方法,这个我们之前是介绍过的:
S = 'abcdefghijklmn'
for c in S[::2]:
print(c, end=',')

a,c,e,g,i,k,m,和文件迭代一样,这里range函数的优点也是明显的,它没有复制字符串,不会在python中再创建一个字符串列表,这对于很大的字符串来说,会节约不少空间。


zip:并行迭代多个序列
内置zip函数允许我们使用for循环来并行迭代多个序列。zip使用多个序列作为参数,然后返回元组的列表,将这些序列中的并排元素一一配对。
L1 = [1,2,3,4,5]
L2 = ['A','B','C','D','E']
for t in zip(L1,L2):
print(t, end=',')

(1, 'A'),(2, 'B'),(3, 'C'),(4, 'D'),(5, 'E'),和range一样,zip在遍历时也是依次按需产生结果,而不是一次性显示所有结果
L1 = [1,2,3,4,5]
L2 = ['A','B','C','D','E']

print(zip(L1,L2))
<zip object at 0x000000000260ED08>同样的,如果想一次性显示所有结果,则必须将其包含在一个list调用中,以便一次性显示所有结果
L1 = [1,2,3,4,5]
L2 = ['A','B','C','D','E']
print(list(zip(L1,L2)))

[(1, 'A'), (2, 'B'), (3, 'C'), (4, 'D'), (5, 'E')]最后只说明一点,当zip的多个参数长度不同时,zip会以最短序列的长度为准来截断所得到的元组
L1 = [1,2,3,4,5]
L2 = ['A','B','C']
print(list(zip(L1,L2)))

[(1, 'A'), (2, 'B'), (3, 'C')]回顾一下,之前我们谈到过,当字典的键和值必须在运行时计算产生时,zip函数可以用于产生这样的字典
keys = ['A', 'B', 'C']
vals = [1, 2, 3]
D = dict(zip(keys,vals))
print(D)

{'C': 3, 'A': 1, 'B': 2}

enumerate:同时产生偏移和元素
有时我们在遍历的时候,既需要偏移值,又需要对应元素,那么内置函数enumerate就可以实现这个功能。
他在for循环的条件下每轮迭代返回一个包含偏移值和偏移元素的元组:(index,value)
S = 'spam'
for t in enumerate(S):
print(t,end=' ')

(0, 's') (1, 'p') (2, 'a') (3, 'm')同样,他也是按需产生,而非一次性产生所有元素的列表
S = 'spam'
print(enumerate(S))

<enumerate object at 0x0000000001ED2558>

关于数据科学更系统、更深入的探讨可进入我们的专栏《Python数据科学之路》:
酱油哥:来吧,一起踏上Python数据科学之路本专栏模仿美剧剧集编排分为五季,第一季:Python编程语言核心基础、第二季:Python数据分析基本工具、第三季:Python语言描述的数学基础、第四季:机器学习典型算法专题、第五季:实战热点深度应用。
回复

使用道具 举报

Python没有什么奇技淫巧吧。。。Hidden features of Python 这个链接上有很多小例子
比如for else值得说下。不break的话就执行else
for i in range(10):
    if i == 10:
        break
    print(i)
else:
    print('10不在里面!')
相当于:
flag = False
for i in range(10):
    if i == 10:
        flag = True
        break
    print(i)
if not flag:
    print('10不在里面!')
说个题外话,还可以看看Writing Idiomatic Python,看看怎么写地道的Python
回复

使用道具 举报

顺势而为47 2019-11-6 22:11:34 显示全部楼层
看了一圈没有人提到的,但是之前在网上不知道什么地方看过,现在又搜了一下,把它贴出来。本篇文章转来转去已经找不到原文链接了,放一下摘抄的链接:17 大 Python 奇技淫巧

1、显示有限的接口到外部当发布python第三方package时,并不希望代码中所有的函数或者class可以被外部import,在__init__.py中添加__all__属性,该list中填写可以import的类或者函数名, 可以起到限制的import的作用, 防止外部import其他函数或者类。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from base import APIBase
from client import Client
from decorator import interface, export, stream
from server import Server
from storage import Storage
from util import (LogFormatter, disable_logging_to_stderr,
                       enable_logging_to_kids, info)
__all__ = ['APIBase', 'Client', 'LogFormatter', 'Server',
           'Storage', 'disable_logging_to_stderr', 'enable_logging_to_kids',
           'export', 'info', 'interface', 'stream']
2、with的魔力
with语句需要支持上下文管理协议的对象, 上下文管理协议包含__enter__和__exit__两个方法。 with语句建立运行时上下文需要通过这两个方法执行进入和退出操作。
其中上下文表达式是跟在with之后的表达式, 该表达式返回一个上下文管理对象。

# 常见with使用场景
with open("test.txt", "r") as my_file:  # 注意, 是__enter__()方法的返回值赋值给了my_file,
    for line in my_file:
        print line详细原理可以查看这篇文章,浅谈 Python 的 with 语句
知道具体原理,我们可以自定义支持上下文管理协议的类,类中实现__enter__和__exit__方法。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
class MyWith(object):
    def __init__(self):
        print "__init__ method"
    def __enter__(self):
        print "__enter__ method"
        return self  # 返回对象给as后的变量
    def __exit__(self, exc_type, exc_value, exc_traceback):
        print "__exit__ method"
        if exc_traceback is None:
            print "Exited without Exception"
            return True
        else:
            print "Exited with Exception"
            return False
def test_with():
    with MyWith() as my_with:
        print "running my_with"
    print "------分割线-----"
    with MyWith() as my_with:
        print "running before Exception"
        raise Exception
        print "running after Exception"
if __name__ == '__main__':
    test_with()执行结果如下:

__init__ method
__enter__ method
running my_with
__exit__ method
Exited without Exception
------分割线-----
__init__ method
__enter__ method
running before Exception
__exit__ method
Exited with Exception
Traceback (most recent call last):
  File "bin/python", line 34, in <module>
    exec(compile(__file__f.read(), __file__, "exec"))
  File "test_with.py", line 33, in <module>
    test_with()
  File "test_with.py", line 28, in test_with
    raise Exception
Exception证明了会先执行__enter__方法, 然后调用with内的逻辑, 最后执行__exit__做退出处理, 并且, 即使出现异常也能正常退出
3、filter的用法
相对filter而言, map和reduce使用的会更频繁一些, filter正如其名字, 按照某种规则过滤掉一些元素

#!/usr/bin/env python
# -*- coding: utf-8 -*-
lst = [1, 2, 3, 4, 5, 6]
# 所有奇数都会返回True, 偶数会返回False被过滤掉
print filter(lambda x: x % 2 != 0, lst)
#输出结果
[1, 3, 5]
4、一行作判断
当条件满足时, 返回的为等号后面的变量, 否则返回else后语句。

lst = [1, 2, 3]
new_lst = lst[0] if lst is not None else None
print new_lst
# 打印结果
1
5、装饰器之单例
使用装饰器实现简单的单例模式

# 单例装饰器
def singleton(cls):
    instances = dict()  # 初始为空
    def _singleton(*args, **kwargs):
        if cls not in instances:  #如果不存在, 则创建并放入字典
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return _singleton
@singleton
class Test(object):
    pass
if __name__ == '__main__':
    t1 = Test()
    t2 = Test()
    # 两者具有相同的地址
    print t1, t2
6、staticmethod装饰器
类中两种常用的装饰, 首先区分一下他们:
    普通成员函数, 其中第一个隐式参数为对象classmethod装饰器, 类方法(给人感觉非常类似于OC中的类方法), 其中第一个隐式参数为staticmethod装饰器, 没有任何隐式参数. python中的静态方法类似与C++中的静态方法
#!/usr/bin/env python
# -*- coding: utf-8 -*-
class A(object):
    # 普通成员函数
    def foo(self, x):
        print "executing foo(%s, %s)" % (self, x)
    @classmethod   # 使用classmethod进行装饰
    def class_foo(cls, x):
        print "executing class_foo(%s, %s)" % (cls, x)
    @staticmethod  # 使用staticmethod进行装饰
    def static_foo(x):
        print "executing static_foo(%s)" % x
def test_three_method():
    obj = A()
    # 直接调用噗通的成员方法
    obj.foo("para")  # 此处obj对象作为成员函数的隐式参数, 就是self
    obj.class_foo("para")  # 此处类作为隐式参数被传入, 就是cls
    A.class_foo("para")  #更直接的类方法调用
    obj.static_foo("para")  # 静态方法并没有任何隐式参数, 但是要通过对象或者类进行调用
    A.static_foo("para")
if __name__ == '__main__':
    test_three_method()

# 函数输出
executing foo(<__main__.A object at 0x100ba4e10>, para)
executing class_foo(<class '__main__.A'>, para)
executing class_foo(<class '__main__.A'>, para)
executing static_foo(para)
executing static_foo(para)7、property装饰器
将property与装饰器结合实现属性私有化(更简单安全的实现get和set方法)。

#python内建函数
property(fget=None, fset=None, fdel=None, doc=None)fget是获取属性的值的函数,fset是设置属性值的函数,fdel是删除属性的函数,doc是一个字符串( 像注释一样)。从实现来看,这些参数都是可选的。
property有三个方法getter(), setter()和delete() 来指定fget, fset和fdel。 这表示以下这行:

class Student(object):
    @property  #相当于property.getter(score) 或者property(score)
    def score(self):
        return self._score
    @score.setter #相当于score = property.setter(score)
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value
8、iter魔法
    通过yield和__iter__的结合,我们可以把一个对象变成可迭代的通过__str__的重写, 可以直接通过想要的形式打印对象
#!/usr/bin/env python
# -*- coding: utf-8 -*-
class TestIter(object):
    def __init__(self):
        self.lst = [1, 2, 3, 4, 5]
    def read(self):
        for ele in xrange(len(self.lst)):
            yield ele
    def __iter__(self):
        return self.read()
    def __str__(self):
        return ','.join(map(str, self.lst))

    __repr__ = __str__
def test_iter():
    obj = TestIter()
    for num in obj:
        print num
    print obj
if __name__ == '__main__':
    test_iter()
9、神奇partial
partial使用上很像C++中仿函数(函数对象)。
在stackoverflow给出了类似与partial的运行方式:

def partial(func, *part_args):
    def wrapper(*extra_args):
        args = list(part_args)
        args.extend(extra_args)
        return func(*args)
    return wrapper利用用闭包的特性绑定预先绑定一些函数参数,返回一个可调用的变量, 直到真正的调用执行:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from functools import partial
def sum(a, b):
    return a + b
def test_partial():
    fun = partial(sum, 2)   # 事先绑定一个参数, fun成为一个只需要一个参数的可调用变量
    print fun(3)  # 实现执行的即是sum(2, 3)
if __name__ == '__main__':
    test_partial()

# 执行结果
510、神秘eval
eval我理解为一种内嵌的python解释器(这种解释可能会有偏差), 会解释字符串为对应的代码并执行, 并且将执行结果返回。
看一下下面这个例子:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
def test_first():
    return 3
def test_second(num):
    return num
action = {  # 可以看做是一个sandbox
        "para": 5,
        "test_first" : test_first,
        "test_second": test_second
        }
def test_eavl():  
    condition = "para == 5 and test_second(test_first) > 5"
    res = eval(condition, action)  # 解释condition并根据action对应的动作执行
    print res
11、exec
    exec在Python中会忽略返回值, 总是返回None, eval会返回执行代码或语句的返回值exec和eval在执行代码时, 除了返回值其他行为都相同在传入字符串时, 会使用compile(source, ‘<string>’, mode)编译字节码。 mode的取值为exec和eval
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def test_first():
    print "hello"
def test_second():
    test_first()
    print "second"
def test_third():
    print "third"
action = {
        "test_second": test_second,
        "test_third": test_third
        }
def test_exec():
    exec "test_second" in action
if __name__ == '__main__':
    test_exec()  # 无法看到执行结果12、getattr
getattr(object, name[, default])返回对象的命名属性,属性名必须是字符串。如果字符串是对象的属性名之一,结果就是该属性的值。例如, getattr(x, ‘foobar’)  等价于 x.foobar。 如果属性名不存在,如果有默认值则返回默认值,否则触发 AttributeError 。

# 使用范例
class TestGetAttr(object):
    test = "test attribute"
    def say(self):
        print "test method"
def test_getattr():
    my_test = TestGetAttr()
    try:
        print getattr(my_test, "test")
    except AttributeError:
        print "Attribute Error!"
    try:
        getattr(my_test, "say")()
    except AttributeError: # 没有该属性, 且没有指定返回值的情况下
        print "Method Error!"
if __name__ == '__main__':
    test_getattr()

# 输出结果
test attribute
test method
13、命令行处理
def process_command_line(argv):
    """
    Return a 2-tuple: (settings object, args list).
    `argv` is a list of arguments, or `None` for ``sys.argv[1:]``.
    """
    if argv is None:
        argv = sys.argv[1:]
    # initialize the parser object:
    parser = optparse.OptionParser(
        formatter=optparse.TitledHelpFormatter(width=78),
        add_help_option=None)
    # define options here:
    parser.add_option(      # customized description; put --help last
        '-h', '--help', action='help',
        help='Show this help message and exit.')
    settings, args = parser.parse_args(argv)
    # check number of arguments, verify values, etc.:
    if args:
        parser.error('program takes no command-line arguments; '
                     '"%s" ignored.' % (args,))
    # further process settings & args if necessary
    return settings, args
def main(argv=None):
    settings, args = process_command_line(argv)
    # application code here, like:
    # run(settings, args)
    return 0        # success
if __name__ == '__main__':
    status = main()
    sys.exit(status)
14、读写csv文件
# 从csv中读取文件, 基本和传统文件读取类似
import csv
with open('data.csv', 'rb') as f:
    reader = csv.reader(f)
    for row in reader:
        print row
# 向csv文件写入
import csv
with open( 'data.csv', 'wb') as f:
    writer = csv.writer(f)
    writer.writerow(['name', 'address', 'age'])  # 单行写入
    data = [
            ( 'xiaoming ','china','10'),
            ( 'Lily', 'USA', '12')]
    writer.writerows(data)  # 多行写入
15、各种时间形式转换只发一张网上的图, 然后查文档就好了, 这个是记不住的
Python 有什么奇技淫巧?-1.jpg
只发一张网上的图, 然后查文档就好了, 这个是记不住的

16、字符串格式化一个非常好用, 很多人又不知道的功能:
>>> name = "andrew"
>>> "my name is {name}".format(name=name)
'my name is andrew'
回复

使用道具 举报

无人岛屿颈 2019-11-6 23:37:49 显示全部楼层
其实 PYC 文件很简单:
>>> import dis, marshal
>>> with open('hello.pyc', 'rb') as f:
...     f.seek(8)
...     dis.dis(marshal.load(f))

比较可以任意连接:
>>> 4 < 10 < 20 < 100
True
来个生成器:
g = (i for i in range(10))

小心默认值陷阱:
>>> def evil(v=[]):
...     v.append(1)
...     print v
...
>>> evil()
[1]
>>> evil()
[1, 1]
来个类:
User = type("User", (object,), {"species": "human"})
PIP 自更新:
$ pip install -U pip
呵呵:
from __future__ import braces
回复

使用道具 举报

素色流年783 2019-11-7 00:12:36 显示全部楼层
30秒内便能学会的30个超实用Python代码片段

1. 唯一性
以下方法可以检查给定列表是否有重复的地方,可用set()的属性将其从列表中删除。
def all_unique(lst):
return len(lst) == len(set(lst))
x = [1,1,2,2,3,2,3,4,5,6]
y = [1,2,3,4,5]
all_unique(x) # False
all_unique(y) # True

2. 变位词(相同字母异序词)
此方法可用于检查两个字符串是否为变位词。
from collections import Counter
def anagram(first, second):
return Counter(first) == Counter(second)
anagram("abcd3", "3acdb") # True

3. 内存
此代码段可用于检查对象的内存使用情况。
import sys
variable = 30
print(sys.getsizeof(variable)) # 24

4. 字节大小
此方法可输出字符串的字节大小。
def byte_size(string):
return(len(string.encode('utf-8')))
byte_size(' ') # 4
byte_size('Hello World') # 11

5. 打印N次字符串
此代码段无需经过循环操作便可多次打印字符串。
n = 2;
s ="Programming";
print(s * n); # ProgrammingProgramming Python 有什么奇技淫巧?-1.jpg

6. 首字母大写
以下代码片段只利用了title(),就能将字符串中每个单词的首字母大写。
s = "programming is awesome"
print(s.title()) # Programming Is Awesome

7. 列表细分
该方法将列表细分为特定大小的列表。
def chunk(list, size):
return [list[i:i+size] for i in range(0,len(list), size)]

8. 压缩
以下代码使用filter()从,将错误值(False、None、0和“ ”)从列表中删除。
def compact(lst):
return list(filter(bool, lst))
compact([0, 1, False, 2, '', 3, 'a', 's', 34]) # [ 1, 2, 3, 'a', 's', 34 ]

9. 计数
以下代码可用于调换2D数组排列。
array = [['a', 'b'], ['c', 'd'], ['e', 'f']]
transposed = zip(*array)
print(transposed) # [('a', 'c', 'e'), ('b', 'd', 'f')]10. 链式比较
以下代码可对各种运算符进行多次比较。
a = 3
print( 2 < a < 8) # True
print(1 == a < 2) # False

11. 逗号分隔
此代码段可将字符串列表转换为单个字符串,同时将列表中的每个元素用逗号隔开。
hobbies = ["basketball", "football", "swimming"]
print("My hobbies are: " + ", ".join(hobbies)) # My hobbies are: basketball, football, swimming

12. 元音计数
此方法可计算字符串中元音(“a”、“e”、“i”、“o”、“u”)的数目。
import re
def count_vowels(str):
return len(len(re.findall(r'[aeiou]', str, re.IGNORECASE))
count_vowels('foobar') # 3
count_vowels('gym') # 0

13. 首字母小写
此方法可将给定字符串的首字母转换为小写模式。
def decapitalize(string):
return str[:1].lower() + str[1:]
decapitalize('FooBar') # 'fooBar'
decapitalize('FooBar') # 'fooBar'

14. 展开列表
下列代码采用了递归法展开潜在的深层列表。
def spread(arg):

    ret = []

    for i in arg:

        if isinstance(i, list):

            ret.extend(i)

        else:

            ret.append(i)

    return ret



def deep_flatten(lst):

    result = []

    result.extend(

        spread(list(map(lambda x: deep_flatten(x) if type(x) == list else x, lst))))

    return result



deep_flatten([1, [2], [[3], 4], 5]) # [1,2,3,4,5]

15. 寻找差异
此方法仅保留第一个迭代中的值来查找两个迭代之间的差异
def difference(a, b):
    set_a = set(a)
    set_b = set(b)
    comparison = set_a.difference(set_b)
    return list(comparison)
difference([1,2,3], [1,2,4]) # [3]

16. 输出差异
以下方法利用已有函数,寻找并输出两个列表之间的差异。
def difference_by(a, b, fn):
    b = set(map(fn, b))
    return [item for item in a if fn(item) not in b]
from math import floor
difference_by([2.1, 1.2], [2.3, 3.4],floor) # [1.2]
difference_by([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], lambda v : v['x']) # [ { x: 2 } ]

17. 链式函数调用
以下方法可以实现在一行中调用多个函数
def add(a, b):
    return a + b
def subtract(a, b):
    return a – b
a, b = 4, 5
print((subtract if a > b else add)(a, b)) # 9   

18. 重复值存在与否
以下方法利用set()只包含唯一元素的特性来检查列表是否存在重复值。
def has_duplicates(lst):
    return len(lst) != len(set(lst))
x = [1,2,3,4,5,5]
y = [1,2,3,4,5]
has_duplicates(x) # True
has_duplicates(y) # False

19. 合并字库
以下方法可将两个字库合并。
def merge_two_dicts(a, b):
    c = a.copy()   # make a copy of a
    c.update(b)    # modify keys and values of a with the ones from b
    return c
a = { 'x': 1, 'y': 2}
b = { 'y': 3, 'z': 4}
print(merge_two_dicts(a, b)) # {'y': 3, 'x': 1, 'z': 4}在Python3.5及升级版中,也可按下列方式执行步骤代码:
def merge_dictionaries(a, b)

   return {**a, **b}

a = { 'x': 1, 'y': 2}

b = { 'y': 3, 'z': 4}

print(merge_dictionaries(a, b)) # {'y': 3, 'x': 1, 'z': 4}

20. 将两个列表转换为字库
以下方法可将两个列表转换为字库。
def to_dictionary(keys, values):

    return dict(zip(keys, values))

keys = ["a", "b", "c"]   

values = [2, 3, 4]

print(to_dictionary(keys, values)) # {'a': 2, 'c': 4, 'b': 3}21. 列举
以下代码段可以采用列举的方式来获取列表的值和索引。
list = ["a", "b", "c", "d"]

for index, element in enumerate(list):

    print("Value", element, "Index ", index, )

# ('Value', 'a', 'Index ', 0)

# ('Value', 'b', 'Index ', 1)

#('Value', 'c', 'Index ', 2)

# ('Value', 'd', 'Index ', 3)   

22. 时间成本
以下代码可计算执行特定代码所需的时间。
import time

start_time = time.time()

a = 1

b = 2

c = a + b

print(c) #3

end_time = time.time()

total_time = end_time - start_time

print("Time: ", total_time)

# ('Time: ', 1.1205673217773438e-05)

23. Try else语句
可将else句作为try/except语句的一部分,如果没有异常情况,则执行else语句。
try:

    2*3

except TypeError:

    print("An exception was raised")

else:

    print("Thank God, no exceptions were raised.")

#Thank God, no exceptions were raised.

24. 出现频率最高的元素
此方法将输出列表中出镜率最高的元素。
def most_frequent(list):

    return max(set(list), key = list.count)



list = [1,2,1,2,3,2,1,4,2]

most_frequent(list)  

25. 回文(正反读有一样的字符串)
以下代码检查给定字符串是否为回文。首先将字符串转换为小写,然后从中删除非字母字符,最后将新字符串版本与原版本进行比对。
def palindrome(string):

    from re import sub

    s = sub('[\W_]', '', string.lower())

    return s == s[::-1]



palindrome('taco cat') # True

26. 不用if-else语句的计算器
以下代码片段展示了如何在不用if-else条件语句的情况下,编写简易计算器。
import operator

action = {

    "+": operator.add,

    "-": operator.sub,

    "/": operator.truediv,

    "*": operator.mul,

    "**": pow

}

print(action['-'](50, 25)) # 25

27. 随机排序
该算法采用Fisher-Yates algorithm对新列表中的元素进行随机排序。
from copy import deepcopy

from random import randint



def shuffle(lst):

    temp_lst = deepcopy(lst)

    m = len(temp_lst)

    while (m):

        m -= 1

        i = randint(0, m)

        temp_lst[m], temp_lst = temp_lst, temp_lst[m]

    return temp_lst



foo = [1,2,3]

shuffle(foo) # [2,3,1] , foo = [1,2,3]

28. 展开列表
此方法将类似javascript中[].concat(…arr)这样的列表展开。
def spread(arg):

    ret = []

    for i in arg:

        if isinstance(i, list):

            ret.extend(i)

        else:

            ret.append(i)

    return ret



spread([1,2,3,[4,5,6],[7],8,9]) # [1,2,3,4,5,6,7,8,9]

29. 交换变量
此方法为能在不使用额外变量的情况下快速交换两种变量。
def swap(a, b):

  return b, a



a, b = -1, 14

swap(a, b) # (14, -1)30. 获取丢失部分的默认值
以下代码可在所需对象不在字库范围内的情况下获取默认值。
d = {'a': 1, 'b': 2}



print(d.get('c', 3)) # 3
本文只简单介绍了一些能在日常工作中帮到我们的方法。但内容都主要立足于GitHub 存储库:https://github.com/30-seconds/30_seconds_of_knowledge,该存储库还包含了有关Python及其他语言和技术行之有效的代码。
————————————————
版权声明:本文为CSDN博主「读芯术」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/duxinshuxiaobian/article/details/102480959
回复

使用道具 举报

幸福341 2019-11-7 01:51:35 显示全部楼层
1. 元类(metaclass)
PyPy的源码里有个pair和extendabletype
"""
Two magic tricks for classes:
    class X:
        __metaclass__ = extendabletype
        ...
    # in some other file...
    class __extend__(X):
        ...      # and here you can add new methods and class attributes to X
Mostly useful together with the second trick, which lets you build
methods whose 'self' is a pair of objects instead of just one:
    class __extend__(pairtype(X, Y)):
        attribute = 42
        def method((x, y), other, arguments):
            ...
    pair(x, y).attribute
    pair(x, y).method(other, arguments)
This finds methods and class attributes based on the actual
class of both objects that go into the pair(), with the usual
rules of method/attribute overriding in (pairs of) subclasses.
For more information, see test_pairtype.
"""

class extendabletype(type):
    """A type with a syntax trick: 'class __extend__(t)' actually extends
    the definition of 't' instead of creating a new subclass."""
    def __new__(cls, name, bases, dict):
        if name == '__extend__':
            for cls in bases:
                for key, value in dict.items():
                    if key == '__module__':
                        continue
                    # XXX do we need to provide something more for pickling?
                    setattr(cls, key, value)
            return None
        else:
            return super(extendabletype, cls).__new__(cls, name, bases, dict)


def pair(a, b):
    """Return a pair object."""
    tp = pairtype(a.__class__, b.__class__)
    return tp((a, b))   # tp is a subclass of tuple

pairtypecache = {}

def pairtype(cls1, cls2):
    """type(pair(a,b)) is pairtype(a.__class__, b.__class__)."""
    try:
        pair = pairtypecache[cls1, cls2]
    except KeyError:
        name = 'pairtype(%s, %s)' % (cls1.__name__, cls2.__name__)
        bases1 = [pairtype(base1, cls2) for base1 in cls1.__bases__]
        bases2 = [pairtype(cls1, base2) for base2 in cls2.__bases__]
        bases = tuple(bases1 + bases2) or (tuple,)  # 'tuple': ultimate base
        pair = pairtypecache[cls1, cls2] = extendabletype(name, bases, {})
    return pair

先说extendabletype。嘛 其实注释已经说得听明白了,就是一个C#里面的partial class的Python实现。
然后是pair和pairtype。pairtype就是根据两个类创建一个新的类,这个类继承自使用这两个类的基类构造的pairtype(有点绕……)或者tuple。

有啥用呢?可以拿来实现multimethod。

class __extend__(pairtype(int, int)):
    def foo((x, y)):
        print 'int-int: %s-%s' % (x, y)

class __extend__(pairtype(bool, bool)):
    def bar((x, y)):
        print 'bool-bool: %s-%s' % (x, y)

pair(False, True).foo()  # prints 'int-int: False, True'
pair(123, True).foo()  # prints 'int-int: 123, True'
pair(False, True).bar()  # prints 'bool-bool: False, True'
pair(123, True).bar()  # Oops, no such method
好像这个例子里元类只是个打辅助的角色,好玩的都在那个pair里……

再换一个。

class GameObjectMeta(type):
    def __new__(mcls, clsname, bases, _dict):
        for k, v in _dict.items():
            if isinstance(v, (list, set)):
                _dict[k] = tuple(v)  # mutable obj not allowed

        cls = type.__new__(mcls, clsname, bases, _dict)
        all_gameobjects.add(cls)
        for b in bases:
            game_objects_hierarchy.add((b, cls))

        return cls

    @staticmethod
    def _dump_gameobject_hierarchy():
        with open('/dev/shm/gomap.dot', 'w') as f:
            f.write('digraph {\nrankdir=LR;\n')
            f.write('\n'.join([
                '"%s" -> "%s";' % (a.__name__, b.__name__)
                for a, b in game_objects_hierarchy
            ]))
            f.write('}')

    def __setattr__(cls, field, v):
        type.__setattr__(cls, field, v)
        if field in ('ui_meta', ):
            return
   
        log.warning('SetAttr: %s.%s = %s' % (cls.__name__, field, repr(v)))

这个是从我写的三国杀游戏中提取的一段代码(点我签名上的链接)。大意就是把class上所有可变的容器都换成不可变的,然后记录下继承关系。
曾经被这个问题坑过,class上的值是全局共享的,逻辑代码一不小心修改了class上的值,单机测试的时候是测不出来的,然后放到线上……就悲剧了……当时绞尽脑汁没有想到是这个问题硬生生的回滚了……发现了问题之后就加上了这个东西,不允许修改class上的东西。
记录下继承关系是为了画类图。

还有就是常用的做数据注入
metadata = {}

def gen_metafunc(_for):
    def metafunc(clsname, bases, _dict):
        meta_for = getattr(_for, clsname)
        meta_for.ui_meta = UIMetaDescriptor()
        if meta_for in metadata:
            raise Exception('%s ui_meta redefinition!' % meta_for)

        metadata[meta_for] = _dict

    return metafunc

from gamepack.thb import characters

__metaclass__ = gen_metafunc(characters.sakuya)


class Sakuya:
    # 于是这个就不是类了, 而是作为数据存到了metadata这个dict里
    char_name = u'十六夜咲夜'
    port_image = 'thb-portrait-sakuya'
    figure_image = 'thb-figure-sakuya'
    miss_sound_effect = 'thb-cv-sakuya_miss'
    description = (
        u'|DB完全潇洒的PAD长 十六夜咲夜 体力:4|r\n\n'
        u'|G月时计|r:|B锁定技|r,准备阶段开始时,你执行一个额外的出牌阶段。\n\n'
        u'|G飞刀|r:你可以将一张装备牌当【弹幕】使用或打出。按此法使用的【弹幕】无距离限制。\n\n'
        u'|DB(画师:小D@星の妄想乡,CV:VV)|r'
    )
Ruby党不要喷,我知道你们可以做的更优雅……

2. Python沙盒逃逸
http://blog.delroth.net/2013/03/escaping-a-python-sandbox-ndh-2013-quals-writeup/
刷新三观的Python代码

3. PEP302 New Import Hook
最近在把刚才提到的纯Python游戏向Unity引擎上移植。
玩过Unity的就会知道,Unity的游戏的资源都是打包在一起的,没有单独的文件,Python解释器就不高兴了……于是写了import hook,用Unity提供的API来读py文件。

# -*- coding: utf-8 -*-

# -- stdlib --
import imp
import sys

# -- third party --
# -- own --
from clr import UnityEngine, WarpGateController

# -- code --

class UnityResourceImporter(object):
    known_builtin = (
        'sys',
        'imp',
        'cStringIO',
        'gevent_core',
        'gevent_ares',
        'gevent_util',
        'gevent_semaphore',
        'msgpack_packer',
        'msgpack_unpacker',
        'UnityEngine',
    )

    def __init__(self, bases, unity_loader):
        self.bases = bases
        self.last_fullname = ''
        self.last_text = ''
        self.last_ispkg = False
        self.unity_load = unity_loader

    def find_module(self, fullname, path=None):
        if fullname in sys.modules:
            return self

        head = fullname.split('.')[0]
        if head in self.known_builtin:
            return None

        rst = self.do_load_module(fullname)
        if rst:
            self.last_text, self.last_ispkg = rst
            self.last_fullname = fullname
            return self
        else:
            return None

    def load_module(self, fullname):
        if fullname in sys.modules:
            return sys.modules[fullname]

        if fullname != self.last_fullname:
            self.find_module(fullname)

        try:
            code = self.last_text
            ispkg = self.last_ispkg

            mod = sys.modules.setdefault(fullname, imp.new_module(fullname))
            mod.__file__ = "<UnityResource: %s>" % fullname
            mod.__loader__ = self

            if ispkg:
                mod.__path__ = []
                mod.__package__ = fullname
            else:
                mod.__package__ = fullname.rpartition('.')[0]

            co = compile(code, mod.__file__, 'exec')
            exec(co, mod.__dict__)

            return mod
        except Exception as e:
            UnityEngine.Debug.LogError('Error importing %s %s' % (fullname, e))
            raise ImportError(e)

    def do_load_module(self, fullname):
        fn = fullname.replace('.', '/')

        asset = self.try_load(fn + '.py')
        if asset is not None:
            return asset, False

        asset = self.try_load(fn + '/__init__.py')
        if asset is not None:
            return asset, True

    def try_load(self, filename):
        for b in self.bases:
            asset = self.unity_load(b + filename)
            if asset is not None:
                return asset

        return None


sys.meta_path.append(UnityResourceImporter([
    'Python/THBattle/',
    'Python/Site/',
    'Python/Stdlib/',
], WarpGateController.GetTextAsset))需要的extension module都静态编译到解释器里了,所以没考虑。

4. 可以批量执行操作的list
class BatchList(list):
    def __getattribute__(self, name):
        try:
            list_attr = list.__getattribute__(self, name)
            return list_attr
        except AttributeError:
            pass

        return list.__getattribute__(self, '__class__')(
            getattr(i, name) for i in self
        )

    def __call__(self, *a, **k):
        return list.__getattribute__(self, '__class__')(
            f(*a, **k) for f in self
        )

class Foo(object):
    def __init__(self, v):
        self.value = v

    def foo(self):
        print 'Foo!', self.value

foo = Foo(1)
foo.foo()  # Foo! 1

foos = BatchList(Foo(i) for i in xrange(10))
foos.value  # BatchList([0, 1, 2, 3, ..., 9])
foos.foo()  # 你能猜到的这个其实不算很黑魔法了,只是感觉很好用也有些危险,所以放上来。

暂时就想到这么多了,以后发现了再补。
回复

使用道具 举报

麻辣鸡翅 2019-11-7 02:26:00 显示全部楼层
大家分享的奇技淫巧的都挺详细的,但是把我能分享的 Python 技巧都给说完了啊啊啊啊啊摔


分享两个技巧吧


第一个,一个是 gc 模块 gc.get_objects()

第二个,如何在项目外部使用 DjangoWeb 源码



先说第一个,gc.get_objects()
可以直接获取当前所有的对象。看起来好像其貌不扬,但是!
通过多次获得当前的所有对象,便可以知道:
1. 不同时刻哪些类型 / 哪些类的数量有多少。
2. 不同时刻占用内存多少。
从而解决了普通程序的内存泄露 Debug 难题。


第二个,如何在项目外部使用 DjangoWeb 源码

在项目外部文件里使用 Django Web 项目源码,比如调用先有的 Django 的 ORM 存取点数据。


第一种方法,使用 sys.path
import os
import sys
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
sys.path.append("/Users/twocucao/Codes/YouProject/youwebapp/")
import django
django.setup()

from app1.tag.models import YouModels

for you_model_ins in YouModel.objects.filter(**data):
        you_model_ins.name = "hahaha"
        you_model_ins.save()

第二种方法,将 DjangoWeb 项目打包并安装,然后
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
import django
django.setup()

from app1.tag.models import YouModels

for you_model_ins in YouModel.objects.filter(**data):
        you_model_ins.name = "hahaha"
        you_model_ins.save()

如果觉得非常有用的话,为什么不关注一下我的专栏 Get 更多 Python 全栈姿势呢?
MG的编程小屋
回复

使用道具 举报

向往草原403 2019-11-7 03:53:52 显示全部楼层
a, b = b, a
回复

使用道具 举报

462710480 2019-11-7 04:34:20 显示全部楼层
我认为Python的库相对于其它语言,都是奇技淫巧。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

  • 0 关注
  • 0 粉丝
  • 4 帖子
广告招商