Python的语言特性

1、Python的函数传参

  Python中所有的变量都可以理解为内存中一个对象的“引用”,或者,也可以看似C中的void *的感觉。这里记住的是类型是属于对象的,而不是变量。对象分为两种:

  可更改的:list,dict;

  不可更改的:strings,tuples,numbers;

  当向函数传递一个参数,即引用的时候:

   1)如果该参数是函数外一个不可变的对象的引用,则函数执行完之后,在函数外打印的是原来的值,与函数对该引用做什么事情无关。

   2)如果该函数是函数外一个可变的对象的引用,则函数执行完之后,在函数外打印的是该函数多该引用所执行的内存空间改变的结果。

2、普通方法、实例方法、类方法和静态方法

  普通方法:在一个Python环境中,独立于类或者对象的函数,可直接导入文件中的方法直接使用。

  实例方法:实例定义来类中,使用之前先通过类实例化一个对象,然后在通过对象调用实例方法。与c++类似。实例方法的第一个参数默认传入一个self,这个self会与实例绑定。

  类方法:与实例方法的调用方式类似,不同的是类方法第一个参数默认传入一个类cls,而不是对象。

  静态方法:对于静态方法其实和普通的方法一样,不需要对谁进行绑定,唯一的区别是调用的时候需要使用a.static_foo(x)或者A.static_foo(x)来调用.

  

  静态方法修饰:@staticmethod

  类方法修饰:@classmethod

 1 #普通方法
 2 def foo(x):
 3     pass
 4 
 5 class A(object):
 6     #实例方法
 7     def foo(self,x):
 8         pass
 9     
10     #类方法
11     @classmethod
12     def class_foo(cls,x):
13         pass
14     
15     #静态方法
16     @staticmethod
17     def static_foo(x):
18         pass

3、类属性和实例属性

  类属性就是供类使用的属性,实例属性就是供实例使用的属性。

  由于Python是动态语言,根据类创建的实例可以任意绑定属性。

  给实例绑定属性的方法是通过实例变量,或者通过self变量:

1 class Student(object):
2     def __init__(self, name):
3         self.name = name
4 
5 s = Student('Bob')
6 s.score = 90

  但是,如果Student类本身需要绑定一个属性呢?可以直接在class中定义属性,这种属性是类属性,归Student类所有

1 class Student(object):
2     name = 'Student'

  当我们定义了一个类属性后,这个属性虽然归类所有,但类的所有实例都可以访问到。来测试一下:

>>> class Student(object):
...     name = 'Student'
...
>>> s = Student() # 创建实例s
>>> print(s.name) # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
Student
>>> print(Student.name) # 打印类的name属性
Student
>>> s.name = 'Michael' # 给实例绑定name属性
>>> print(s.name) # 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性
Michael
>>> print(Student.name) # 但是类属性并未消失,用Student.name仍然可以访问
Student
>>> del s.name # 如果删除实例的name属性
>>> print(s.name) # 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了
Student

  从上面的例子可以看出,在编写程序的时候,千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性

4、Python自省

  自省就是面向对象的语言所写的程序在运行时,所能知道对象的类型.简单一句就是运行时能够获得对象的类型.比如type(),dir(),getattr(),hasattr(),isinstance()

5、python的各种推导式(列表推导式、字典推导式、集合推导式) 

  推导式comprehensions(又称解析式),是Python的一种独有特性。推导式是可以从一个数据序列构建另一个新的数据序列的结构体。 共有三种推导,在Python2和3中都有支持:

  • 列表(list)推导式
  • 字典(dict)推导式
  • 集合(set)推导式

  使用[]生成list

  基本格式

variable = [out_exp_res for out_exp in input_list if out_exp == 2]
  out_exp_res:  列表生成元素表达式,可以是有返回值的函数。
  for out_exp in input_list:  迭代input_list将out_exp传入out_exp_res表达式中。
  if out_exp == 2:  根据条件过滤哪些值可以。

注意:表达式是用方括号扩起来,说明生成的是一个列表。

实例一:
multiples = [i for i in range(30) if i % 3 is 0]
print(multiples)
# Output: [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
实例二:

def squared(x):
    return x*x
multiples = [squared(i) for i in range(30) if i % 3 is 0]
print multiples
#  Output: [0, 9, 36, 81, 144, 225, 324, 441, 576, 729]

  使用()生成generator

  将列表推导式的 [] 改成 () 即可得到生成器。以下会介绍生成器。

multiples = (i for i in range(30) if i % 3 is 0)
print(type(multiples))
#  Output: <type 'generator'>

  

  字典推导式

   字典推导和列表推导的使用方法是类似的,只不中括号该改成大括号。

  基本格式

variable = {out_exp_res for out_exp in input_list if out_exp == 2}
  out_exp_res:  列表生成元素表达式,可以是有返回值的函数。
  for out_exp in input_list:  可迭代的input_list将out_exp传入out_exp_res表达式中。
  if out_exp == 2:  根据条件过滤哪些值可以。

例子一:大小写key合并

mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
mcase_frequency = {
    k.lower(): mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0)
    for k in mcase.keys()
    if k.lower() in ['a','b']
}
print mcase_frequency
#  Output: {'a': 17, 'b': 34}

例子二:快速更换key和value

mcase = {'a': 10, 'b': 34}
mcase_frequency = {v: k for k, v in mcase.items()}
print mcase_frequency
#  Output: {10: 'a', 34: 'b'}

  集合推导式

  它们跟列表推导式也是类似的。 唯一的区别在于它使用大括号{}。

squared = {x**2 for x in [1, 1, 2]}
print(squared)
# Output: set([1, 4])

6、生成器generator

  通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

  所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

  要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:在第五点中有陈述

  如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值

>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

  不断调用next(g)实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象

>>> g = (x * x for x in range(10))
>>> for n in g:
...     print(n)
... 
0
1
4
9

 第二种generator的获取方法:

  比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

  1, 1, 2, 3, 5, 8, 13, 21, 34, ...

  斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n = n + 1
    return 'done'

  仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

 也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'

  如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。

  这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

  同样的,把函数改成generator后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代:

  >>> for n in fib(6):
  ...     print(n)

 但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIterationvalue中:
 
  >>> g = fib(6)
  >>> while True:
  ...     try:
  ...         x = next(g)
  ...         print('g:', x)
  ...     except StopIteration as e:
  ...         print('Generator return value:', e.value)
  ...         break
  ...
  g: 1
  g: 1
  g: 2
  g: 3
  g: 5
  g: 8
  Generator return value: done

7、Python中单下划线和双下划线  

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

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

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

8、字符串格式化:%和format函数格式化的用法

 .format在许多方面看起来更便利.对于%最烦人的是它无法同时传递一个变量和元组.你可能会想下面的代码不会有什么问题:

 

 但是,如果name恰好是(1,2,3),它将会抛出一个TypeError异常.为了保证它总是正确的,你必须这样做:

 

 但是有点丑..format就没有这些问题.你给的第二个问题也是这样,.format好看多了.

  

  自python2.6开始,新增了一种格式化字符串的函数str.format(),可谓威力十足。那么,他跟之前的%型格式化字符串相比,有什么优越的存在呢?让我们来揭开它羞答答的面纱。
 详见如下文章:http://www.jb51.net/article/63672.htm

 

9、迭代器和生成器

   看廖雪峰的Python教程:

   迭代器:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143178254193589df9c612d2449618ea460e7a672a366000

    凡是可作用于for循环的对象都是Iterable类型;

    凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

    集合数据类型如listdictstr等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

    Python的for循环本质上就是通过不断调用next()函数实现的,例如:

   生成器:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014317799226173f45ce40636141b6abc8424e12b5fb27000  

    generator是非常强大的工具,在Python中,可以简单地把列表生成式改成generator,也可以通过函数实现复杂逻辑的generator。

    要理解generator的工作原理,它是在for循环的过程中不断计算出下一个元素,并在适当的条件结束for循环。对于函数改成的generator来说,遇到return语句或者执行到函数体最后一行语句,就是结束generator的指令,for循环随之结束。

    请注意区分普通函数和generator函数,普通函数调用直接返回结果:

    >>> r = abs(6)
    >>> r
    6

    generator函数的“调用”实际返回一个generator对象:

    >>> g = fib(6)
    >>> g
    <generator object fib at 0x1022ef948>

10、*args and **kwargs(可变参数)

  用*args**kwargs只是为了方便并没有强制使用它们.

  当你不确定你的函数里将要传递多少参数时你可以用*args.例如,它可以传递任意数量的参数:

def print_everything(*args):
    for count,thing int enumerate(args):
        print '{0},{1}'.format(count,thing);
print_everything('apple','banana','cabbage');

output:
0,apple
1,banana
2,cabbage

  相似的,**kwargs允许你使用没有事先定义的参数名:

原文地址:https://www.cnblogs.com/houjun/p/8671689.html