Python 简单小知识

Python 知识点不完全总结

Posted by Sylvia on January 23, 2018

1. Python函数参数

1. 位置参数

调用时根据定义参数的顺序传参,如下例:

def fun(a, b):
    return a-b
fun(1, 2)      # 结果为 1-2 = -1

2. 默认参数

定义函数时写入默认参数,即便不传参也不会显示错误,如下例:

def function (param = 0)
    return param

规范: 将默认的、变化不大的写在后面,变化大的参数写在前面

3. 可更改与不可更改参数

所有的变量都可以理解是内存中一个对象的“引用”,而对象有两种,“可更改”(mutable)与“不可更改”(immutable)对象。在python中,strings, tuples, 和numbers是不可更改的对象,而list,dict等则是可以修改的对象,看下例:

a = 1
def fun(a):
    a = 2
print a     # 结果为1,不会随函数调用而更改
a = []
def fun(a):
    a.append(1)
print a     # 结果为[1],随函数调用而更改

4. 可变参数

可变参数在函数调用时自动组装成tuple,直接上栗子:

定义:

def fun(*args):
    pass 

调用方法一:

fun(1,2,3)

调用方法二:

params = [1,2,3]
fun(*params)

5. 关键字参数

关键字参数在函数调用时自动组装成dict, 可不传,也可传入任意个数的关键字参数(必须传入参数名),上栗子:

定义:

def fun(**kwargs):
    return kw 

调用方法一:

fun(city = 'Beijing')  # 返回{'city':'Beijing'}

调用方法二:

fun(gender ='M', job = 'Coder')  # 返回{'gender':'M', 'job':'Coder'}

备注:当传入一个dict时,函数内部对其修改不会影响函数外的dict

6. 命名关键字参数

限制了传入的关键字参数,只接受固定名称参数传入,如下:

def fun(paraA, paraB, *, keyParaA, keyParaB)
    pass

则只有key为keyParaA和keyParaB的关键字函数可以传入。

7. *args and **kwargs

当不确定函数里将要传递多少参数时你可以用*args,相似的,**kwargs允许使用没有事先定义的参数名;
你也可以混着使用,命名参数首先获得参数值然后所有的其他参数都传递给*args**kwargs,命名参数在列表的最前端,如:

def table_things(titlestring, **kwargs)

*args**kwargs可以同时在函数的定义中,但是*args必须在**kwargs前面。

2. Python方法

Python有三种方法,分别是实例方法,静态方法(staticmethod)以及类方法(classmethod),结合下例说明一下这三种方法的区别:

def fun(x):
    print "executing fun(%s)"%(x)

class A(object):
    def fun(self,x):
        print "executing fun(%s,%s)"%(self,x)

    @classmethod
    def class_fun(cls,x):
        print "executing class_fun(%s,%s)"%(cls,x)

    @staticmethod
    def static_fun(x):
        print "executing static_fun(%s)"%x

a=A()

接下来我们来结合栗子对这三种方法做一下讲解。
对于实例方法,在类里每次定义方法的时候都需要绑定一个实例,在上栗中,fun()就是实例方法,它的调用离不开实例,所以我们把实例自己(self) 作为参数传给方法,便能进行方法与实例的绑定;
显然易见,class_fun()就是类方法了,而它则是通过cls来绑定了类;
而静态方法则不需要对谁进行绑定,那么这三种方法在调用时有什么区别呢?

|实例方法 类方法 静态方法  
a = A() a.fun(x) a.class_fun(x) a.static_fun(x)
A 不可用 A.class_fun(x) A.static_fun(x)

3. Python中的下划线

废话不说直接上栗子:

>>> class MyClass():
...     def __init__(self):
...             self.__superprivate = "Hello"
...             self._semiprivate = ", world!"
...
>>> mc = MyClass()
>>> print mc.__superprivate
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: myClass instance has no attribute '__superprivate'
>>> print mc._semiprivate
, world!
>>> print mc.__dict__
{'_MyClass__superprivate': 'Hello', '_semiprivate': ', world!'}

__foo__:只是约定,Python内部的名字,用来区别其他用户自定义的命名,以防冲突。

_foo:只是约定,用来指定变量私有.程序员用来指定私有变量的一种方式;

__foo:这个有真正的意义:解析器用_classname__foo来代替这个名字,以区别和其他类相同的命名。

4. Python装饰器

1. 两点定义:

  • 前有@,将之后出现的函数作为参数传入
  • 返回作为参数传入的函数

2、 栗子

def log (func):
    @functools.wraps (func)       
    def wrapper (*args, **kw):
        print ( 'call %s' %func.name)
        return func (*args, **kw)
    return wrapper
       
@log
def now ( ):
    print ('2017-05-09')

3. 说明

在上栗中,调用now()时,now()方法会作为参数传入log()方法,实际上是调用了以now()为参数的log()方法;
上栗的打印内容是:

call now ( ):
2017-05-09

这么解释或许更加专业,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数log(now),返回wrapper()函数。

5. Lambda

Lambda又称匿名函数,当我们需要使用一个简单的且无需重复调用的函数时,就可以使用lambda,优雅又简洁。其使用方法如下例:

lambda 参数 : 返回值

是不是超简单,那看一下具体使用吧:

map( lambda x: x*x, [y for y in range(10)] )

上述代码等价于:

def sq(x):
    return x * x

map(sq, [y for y in range(10)])

使用lambda可以有效减少代码量,这样的写法是将「遍历列表,给遇到的每个元素都做某种运算」的过程从一个循环里抽象出来成为一个函数 map,然后用 lambda 表达式将这种运算作为参数传给 map。Python 之中,类似能用到 lambda 表达式的「高级」函数还有sort,reduce,filter等等。

6. map(),reduce(),filter()

既然说到了lambda,那就不得不提map()、reduce()和filter(),下面就一一介绍一下吧:

1. map()

map()函数接收两个参数,一个是函数,一个是序列,map将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回。
比如我们有一个函数f(x)=x*x,要把这个函数作用在一个list [1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用map()实现如下:

map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])

map()传入的第一个参数是f,即需要对列表中的元素进行的操作,第二个参数是迭代列表,返回则是一个新的经过处理后的列表。 map()作为高阶函数,将运算规则抽象了,将列表中的数字转为字符串只需要一行代码:

map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])

2. reduce()

与map()相同,reduce()同样接收两个参数,第一个是函数(这个函数必须接收两个参数),第二个是迭代列表,它的效果如下:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

即列表中的第一个元素与第二个元素作为参数传入f()的返回值,与列表中的第三个元素再次作为参数传入f()……

3. filter()

filter()函数用于过滤序列。
又和map()类似的是,filter()也接收一个函数和一个序列。和map()不同的时,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素,上栗子:

def is_odd(n):
    return n % 2 == 1

filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])

# 结果: [1, 5, 9, 15]

7. Python中的拷贝

上个栗子一目了然:

import copy
a = [1, 2, 3, 4, ['a', 'b']]  #原始对象

b = a  #赋值,传对象的引用
c = copy.copy(a)  #对象拷贝,浅拷贝
d = copy.deepcopy(a)  #对象拷贝,深拷贝

a.append(5)  #修改对象a
a[4].append('c')  #修改对象a中的['a', 'b']数组对象

print 'a = ', a
print 'b = ', b
print 'c = ', c
print 'd = ', d

输出结果:
a =  [1, 2, 3, 4, ['a', 'b', 'c'], 5]
b =  [1, 2, 3, 4, ['a', 'b', 'c'], 5]
c =  [1, 2, 3, 4, ['a', 'b', 'c']]
d =  [1, 2, 3, 4, ['a', 'b']]

8. Python中的正则表达式

1.预设元字符

  • \w 匹配任意一个单词字符,包括数字和下划线,等价于[A-Za-z0-9_],例如 a\wc 可以匹配 abc、acc
  • \W 匹配任意一个非单词字符,与 \w 操作相反,它等价于 [^A-Za-z0-9_],例如: a\Wc 可匹配 a!c
  • \s 匹配任意一个空白字符,空格、回车等都是空白字符,例如:a\sc 可以配 a\nc,这里的 \n表示回车
  • \S 匹配任意一个非空白字符
  • \d 匹配任意一个数字,它等价于[0-9],例如:a\dc 可匹配 a1c、a2c …
  • \D 匹配任意一个非数字

2.基本元字符

  • . 匹配除换行符以外的任意一个字符,例如:”a.c” 可以完全匹配 “abc”,也可以匹配 “abcef” 中的 “abc”
  • \ 转义字符,使特殊字符具有本来的意义,例如: 1.2 可以匹配 1.2
  • [...] 匹配方括号中的任意一个字符,例如:a[bcd]e 可以匹配 abe、ace、ade,它还支持范围操作,比如:a到z可表示为 “a-z”,0到9可表示为 “0-9”,注意,在 “[]” 中的特殊字符不再有特殊意义,就是它字面的意义,例如:[.*]就是匹配 . 或者 *
  • [^...] 字符集取反,表示只要不是括号中出现的字符都可以匹配,例如:a[^bcd]e 可匹配 aee、afe等

3.重复匹配

  • * 重复匹配零次或者更多次
  • ? 重复匹配零次或者一次
  • + 重复匹配1次或者多次
  • {n} 重复匹配n次
  • {n,} 重复匹配至少n次
  • {n, m} 重复匹配n到m次

4.贪婪与非贪婪

  • 贪婪模式 正则表达式重复匹配时,在使整个表达式能得到匹配的前提下尽可能匹配多的字符,我们称之为贪婪模式;
    例如: r”a.*b” 表示匹配 a 开头 b 结尾,中间可以是任意多个字符的字符串,如果用它来匹配 aaabcb,那么它会匹配整个字符串
  • 非贪婪模式 只需要在量词后面加一个问号” ?”,在保证匹配的情况下尽可能少的匹配;
    比如刚才的例子,我们只希望匹配 aaab,那么只需要修改正则表达式为 r”a.*?b”

5.正则引擎

  • re.match(pattern, string)
    match方法从字符串的起始位置开始检查,如果刚好有一个子字符串与正则表达式相匹配,则返回一个Match对象,只要起始位置不匹配则退出,返回 None

  • re.search(pattern, string)
    search 方法虽然也是从起始位置开始检查,但是它在起始位置不匹配的时候会一直尝试往后检查,直到匹配为止,如果到字符串的末尾还没有匹配,则返回 None

  • re.findall(pattern, string) findall 返回的对象是由匹配的子字符串组成的列表,即使匹配到了也不中断,继续匹配

写了那么多,但每次写正则的时候,我还是会用度娘 =.=