python语法

python一些语法总结。

Python语法

高阶函数

既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数

  1. 匿名函数lambda

    关键字lambda表示匿名函数,冒号前面的x表示函数参数。
    匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。
    用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象.

  2. map有两个参数,第一个是函数对象,第二个是一个可迭代对象。map将函数依次作用于序列的每一个元素。返回一个迭代器(不能直接用print输出)。

  3. reduce函数位于functools模块中,同样接受两个参数,一个函数对象一个可迭代序列,和map不同之处在于reduce处理完一个元素会将结果累计至下一个元素。

    from functools import reduce

    def fn(x,y):
    return 10*x+y
    L=[i for i in range(10)]
    res1=reduce(fn,L)

    上述代码计算了1~9的累加。

    def fn(x,y):
    return 10*x+y
    def char_to_num(s):
       return {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9}[s]
    print(reduce(fn,map(char_to_num,'123456')))

    上述代码实现了字符串转化为整数

    CHAR_TO_FLOAT = {
       '0': 0,
       '1': 1,
       '2': 2,
       '3': 3,
       '4': 4,
       '5': 5,
       '6': 6,
       '7': 7,
       '8': 8,
       '9': 9,
       '.': -1
    }
    def char_to_float(s):
       nums=map(lambda ch:CHAR_TO_FLOAT[ch],s)
       point=0
       def to_float(x,y):
           nonlocal point
           if y==-1:
               point=1
               return x
           if point==0:
               return 10*x+y
           else:
               point*=10
               return x+y/point
       return reduce(to_float,nums,0.0)

    字符串转化为float,reduce有第三个可选参数,作为初始的默认值,从这个默认值开始计算。如果没给且序列只有一个元素,reduce返回第一个元素。

  4. filter()函数用于过滤序列,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。返回的也是一个迭代器。

    下面用埃氏筛法求素数

    def odd_iter():
       #生成一个奇数序列
       n=1
       while True:
           n+=2
           yield n
    
    
    #过滤函数
    
    def not_divisible(n):
       return lambda x:x%n>0
    
    def primes():
       yield 2
       it=odd_iter()#初始化为奇数序列
       while True:
           n=next(it)
           yield n
           it=filter(not_divisible(n),it)#每次过滤掉n的倍数
  5. sorted()函数可以对序列进行排序,若要定制个性化操作,可以传入关键字参数key和reverse等

    例如key=abs就是按绝对值排序,key=str.lower就是把原始序列变成小写再排序即忽略大小写

  6. 高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。相关参数和变量都保存在返回的函数中——闭包(Closure)的程序结构,返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

装饰器

在代码运行过程中不改变函数定义而动态增加函数功能的特性,称为装饰器(Decorator)。

函数对象有个name属性可以得到函数对象的名字。

本质上,decorator就是一个返回函数的高阶函数。

看以下代码:

def log(func):
    @functools.wraps(func)
    def wrapper(*args,**kargs):
        print('call %s' % func.__name__)
        return func(*args,**kargs)
    return wrapper

@log
def now():
    print('2017-2-18')

加上@log之后,相当于执行了now = log(now),now不再指向原来的函数,而是log函数中返回的wrapper函数。wrapper()函数的参数定义是(*args, **kwargs),因此,wrapper()函数可以接受任意参数的调用。

如果装饰器本身就需要参数,那就得套三层了。也就是说第一个函数返回装饰器,如下:

def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

@log('execute')
def now():
    print('2017-2-18')

经过装饰之后的函数属性会变化,如name@functools.wraps(func)语句用于修正。

在面向对象(OOP)的设计模式中,decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。Python的decorator可以用函数实现,也可以用类实现。

slots以及运算符重载

python中当创建一个类或者实例之后,均可以再给它添加方法或者属性
给类添加方法和属性只需要用类名.属性或方法名=属性或方法即可,给实例添加属性也类似,只是给实例添加方法时麻烦点:

class student:
    pass

s=student()
from types import MethodType

s.age=MethodType(age,s)
s.age(25)

可以在定义类时利用slots属性设置可以添加的属性。比如,上述student类:

class student(object):
    __slots__=('name','age')

这样的话就不能添加别的属性了。

使用_slots_要注意,_slots_定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。

除非在子类中也定义_slots_,这样,子类实例允许定义的属性就是自身的_slots_加上父类的_slots_


看到类似slots这种形如xxx的变量或者函数名就要注意,这些在Python中是有特殊用途的。
slots我们已经知道怎么用了,还有好多方法。

  1. strrepr

    两者的区别是str()返回用户看到的字符串,而repr()返回程序开发者看到的字符串(print调用str,直接在交互式解释器输出调用的是repr),也就是说,repr()是为调试服务的。
    但是通常str()和repr()代码都是一样的,所以,有个偷懒的写法:
    __repr__ = __str__

  2. 如果一个类想被用于for … in循环,类似list或tuple那样,就必须实现一个iter()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的next()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。

    要达到多个迭代器的效果,_iter_只需返回新的对象而不是本身

    class Iterator:
    def __init__(self,data):
       self.data=data
       self.index=-1
       self.end=len(data)
    def __iter__(self):
       """
       if iter(self.data) is iter(self.data):
           print('ha')#返回的并不是对象本身,所以可以使用多个迭代器
       return iter(self.data)#iter针对容器会返回迭代器,针对迭代器会返回它本身
       """
       return self.data #一次性的迭代器
    def __next__(self):
       self.index += 1
       if self.index==self.end:
           raise StopIteration
       return self.data[self.index]
    def __back__(self):
       self.index-=1
       if self.index==-1:
           raise StopIteration
       return self.data[self.index]
    class container:
       def __init__(self,data):
           self.data=data
       def __iter__(self):
           return Iterator(data)
  3. getitem用于索引、分片,setitem索引赋值,delitem索引和分片删除

  4. __getattr__

    当调用不存在的属性时,Python解释器会试图调用getattr(self, ‘score’)来尝试获得属性score

    class Student(object):
    def __init__(self):
       self.name = 'Michael'
    
    def __getattr__(self, attr):
       if attr=='score':
           return 99
  5. __call__

    __call__()还可以定义参数。对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。

    通过callable()函数,我们就可以判断一个对象是否是“可调用”对象。

继承、@property和super

还记得装饰器(decorator)可以给函数动态加上功能吗?对于类的方法,装饰器一样起作用。Python内置的@property装饰器就是负责把一个方法变成属性调用的.

@property

把一个getter方法变成属性,只需要加上@property就可以了。如果还要求属性可写,就再设置setter方法。如下所示:

class Student(object):
    def __init__(self,_score):
        self.score=_score
    @property
    def score(self):
        return self._score

    @score.setter
    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
    @score.deleter
    def score(self):
        raise AttributeError("can't delete attribute")

其实,完全可以定义两个函数,set和get来处理,这样操作时调用相应方法即可。装饰器的重要特性就是它看起来像一个普通的属性,但是根据访问它的不同方式会触发不同方法,这使得实例的接口变得非常单一。

这里可以看到,三个相互关联的方法有着相同的名称score,这个表示的就是属性(构造函数里的属性赋值也会触发检查),其它两个方法将可选的setter和deleter函数附加到了score属性上。

一个oop的例子

python的继承、封装很好理解,和其它语言如出一辙,对于多态而言,由于python是动态类型的语言,看如下代码:

class Base:
    def __init__(self,x):
        self.x=x
    def method(self):
        print('In base')


class Sub(Base):
    def __init__(self,x,y):
        super().__init__(x)
        self.y=y
    def method(self):
        print('start sub')
        Base.method(self)
        print('end sub')


x=Base(5)
y=Sub(5,10)

def test(obj):
    obj.method()

test(x)
test(y)
print(x.x)
print(y.x,y.y)

输出结果如下:

In base
start sub
In base
end sub
5
5 10

对于静态语言(例如Java)来说,如果需要传入Base类型,则传入的对象必须是Base类型或者它的子类,否则,将无法调用method()方法。

对于Python这样的动态语言来说,则不一定需要传入Base类型。我们只需要保证传入的对象有一个method()方法就可以了,这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。python变量可以引用一切对象,所以多态性更灵活!

注意,这里推荐使用super()函数调用父类方法而不是直接用父类名(虽然在这个地方效果一样),但是多继承时会有点问题。

super()函数

看以下多继承的例子(python支持多重继承,java只有单继承,c++也是可以多重继承):

class Root(object):
    def __init__(self):
        print('this is root!')

class A(Root):
    def __init__(self):
        print('enter A')
        super().__init__()
        print('exit A')

class B(Root):
    def __init__(self):
        print('enter B')
        super().__init__()
        print('exit B')

class C(A,B):
    def __init__(self):
        print('enter C')
        super().__init__()
        print('exit C')

c=C()

print(c.__class__.mro())



上述代码的输出如下:
enter C
enter A
enter B
this is root!
exit B
exit A
exit C
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Root'>, <class 'object'>]

super使用了方法解析顺序(Method Resolution Order,或MRO),会对这个元组进行依次搜索,这个元组用于确定一个方法是在当前类还是父类即扫描顺序。用类的__mro__属性或者mro()方法都可获得这个元组。至于这个元组怎么得到的可以看下面的链接,介绍c3算法:
了解更多mro以及c3算法

下面分析一下上面的代码

由解析顺序可以看到,首先初始化C类,然后由super调用会调用A类的构造函数,A类又继续调用B类的构造函数,B类最后调用Root类的构造函数。总之,super干的事可以总结为下面的代码:

def super(cls,inst):
    mro=inst.__class__.mro()
    return mro[mro.index(cls)+1]
第一个参数是确定当前类的位置,第二个参数用于得到mro元组。

也就是返回mro元组的下一个类。这里,super有两个参数,上述代码没添加参数,文档中说了一点:

The zero argument form only works inside a class definition, as the compiler fills in the necessary details to correctly retrieve the class being defined, as well as accessing the current instance for ordinary methods.

大意是说零参数形式仅在类定义中工作,编译器会填充必要的细节。这里也可以加上参数。如下:

class Root(object):
    def __init__(self):
        print('this is root!')

class A(Root):
    def __init__(self):
        print('enter A')
        super(A,self).__init__()
        print('exit A')

class B(Root):
    def __init__(self):
        print('enter B')
        super(B,self).__init__()
        print('exit B')

class C(A,B):
    def __init__(self):
        print('enter C')
        super(C,self).__init__()
        print('exit C')

结果完全一样的。

其实,super有两个典型的用例。

  • 在具有单继承的类层次结构中,super可以用于引用父类而不明确命名它们,从而使代码更易于维护。这种使用与其他编程语言中super的使用非常相似。
  • 第二种用例是在动态执行环境中支持协作多重继承。此用例是Python独有的,在静态编译语言或只支持单继承的语言中找不到。这使得可以实现“菱形图”,其中多个基类实现相同的方法。 (如上示例,如果全部不用super而用类名,会发现Root类的构造函数被调用了两次)

现在水平有限,容日后再更吧~/(ㄒoㄒ)/

最后一点,不论是c3还是mro啦都是新式类(new-style class)的特性,如果不是那就( ^_^ )/~~拜拜。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值