Loading

a byte of python读后总结

A BYTE OF PYTHON

Python中万物皆对象

变量

对应在计算机内存开辟的部分空间,以便保存数据,赋值时不需要指定类型,可以随意改变(类似指针)。为了访问变量,用标识符取名。给变量赋值的数字和字符串都叫字面值常量。实际上赋值时是先创建对应数据类型的对象,再让变量引用(指向)它。复制序列或复杂对象时记得用切片[:]

数据类型

变量中存储的数据的类型:

  • 数字(不可变)

    • 整形5
    • 浮点3.23 52.3E-4
  • 复数9.32e-36j

  • 字符串(不可变)

    format()格式化方法

    • print('{0} was {1} years old when he wrote this book'.format(name, age)):0、1可省略,name、age为变量
      
    • print('{name} was {age} years old when he wrote this book'.format(name=name, age=age))
      
    • print(f'{name} was {age} years old when he wrote this book')
      
  • 列表(可变)

  • 元组(可变)

    一组字符串可以通过链接字符串.join()合并成一个

  • 字典(可变)

函数

函数参数

对于不可变类型数据的变量传递类似Java的值传递,可变的类似为引用传递。但严格来说都是传递了值(变量赋值时所对应的内存空间),只是在函数域内对不可变类型重新赋值时,会新开辟一个空间给局部变量,所以不会影响传进去的变量本身。

关键字参数

传实参的时候指明形参的名字func(1,a=2, c=3)

可变参数

  • *参数名:收集从该点到最后的所有位置参数以tuple形式传入
  • **参数名:收集从该点到最后的所有关键字参数以dictionary形式传入

作用域

局部变量、全局变量的概念和其他语言差不多,如果在局部作用域内(函数、类)想用最外层变量(全局变量),要用global声明一下。最好少用,会使变量定义变得混乱。

模块

  • 模块是复用的函数、类、变量的集合,可以用直接.py文件编写后使用或者解释器的编写语言编写后编译后使用。

  • 模块存放应该考虑目录的问题:如果是内置模块,解释器知道在哪找;如果不是,解释器会在sys.path包含的目录列表中找(第一个是空的,表示当前目录(程序启动目录))。找到后会先将模块的代码第一次import运行一次,这样在使用模块的文件中就可以使用模块的功能了。

  • 字节码.pyc文件:每次import太耗时耗力,将模块先编译成.pyc文件,使用时免去很多处理工作(python文件先转换为字节文件再变为原生机器语言)

  • __name__是Python的内置变量,用于指代当前模块。当哪个模块被直接执行时,该模块__name__的值就是__main__,当被导入另一模块时,__name__的值就是模块的真实名称。通过__name__属性可以确定模块是单独跑的还是被载入,以此添加一些测试代码。

    if __name__ == '__main__':
        print('This program is being run by itself')
    else:
        print('I am being imported from another module')
    

    对于很多编程语言来说,程序都必须要有一个入口,比如 C,C++,以及完全面向对象的编程语言 Java,C# 等。C 和 C++ 都需要有一个 main 函数来作为程序的入口,也就是程序的运行会从 main 函数开始。同样,Java 和 C# 必须要有一个包含 Main 方法的主类来作为程序入口。而 Python 则有不同,它属于脚本语言,不像编译型语言那样先将程序编译成二进制再运行,而是动态的逐行解释运行。也就是从脚本第一行开始运行,没有统一的入口。

包只是带有特殊__init__.py文件的模块文件夹,该文件向Python表明这个文件夹是特殊的,它包含Python模块。当import该目录时,就会执行__init.py__里的代码,因此可以用来控制模块的导入1.在该文件中指定默认import的模块,这样就不用一个一个导入模块了。(注意:执行import时当前目录是不会变得,所以需要输入完整的包名)。2.另外当使用 from xxxpackage import *时实际上是根据__init__.py里定义的__all__=['xxxmodule, 'yyymodule']变量

面向对象

变量

对象可以使用属于该对象的普通变量来存储数据。 属于对象或类的变量(对象变量:用self修饰;类变量:在所有函数外定义)称为字段

  • 对象变量(实例属性):用self修饰赋值,与类变量同名时会隐藏掉类变量(当然类变量声明后在函数也是用self.var访问的)。
  • 类变量(类属性):共享单车(不是)所有对象都能访问。在类定义中所有函数外定义

约定俗成:任何仅在类或对象中使用的变量都应该以双下划线开头,所有其他名称都是公共的。在内部,python使用一种 name mangling 技术,将 __membername替换成 _classname__membername,所以你在外部使用原来的私有成员的名字时,会提示找不到。

self

对象方法的第一个形参必须为self(虽然实际上什么名字都行),调用时不需要给出该参数的值,Python自己会提供这个值(引用对象本身)(和C++、Java的this一样)myobject.method(arg1, arg2)➡MyClass.method(myobject, arg1, arg2)

同时self.__class__代表类的名字

__init__(self,...)

构造函数

方法

  • 对象(实例)方法:没有修饰,最少包含一个self参数

  • 类方法:@classmethod 修饰,它最少也要包含一个参数,通常将其命名为 cls,Python 会自动将类本身绑定给 cls 参数(注意,绑定的不是实例对象)。可以访问类属性,无法访问实例属性

  • 静态方法: @staticmethod修饰,不需要特殊参数。无法访问类属性、实例属性。

    类的专有方法(可重载):

    • __init__ : 构造函数,在生成对象时调用
    • __del__ : 析构函数,释放对象时使用
    • __repr__ : 打印,转换
    • __setitem__ : 按照索引赋值时调用
    • __getitem__: 按照索引获取值时调用
    • __len__: len()方法调用以获得序列长度
    • __cmp__: 比较运算
    • __call__: 函数调用
    • __add__: 加运算
    • __sub__: 减运算
    • __mul__: 乘运算
    • __truediv__: 除运算
    • __mod__: 求余运算
    • __pow__: 乘方

返回多个值可以用元组

继承

class Classname(Father1,Father2...)

为了使用继承,在类定义中的类名之后的元组中指定基类名。接下来,我们观察到使用 self 变量作为参数显式调用基类的__init__方法,这样我们就可以在子类中初始化实例的基类部分。因为如果子类中定义了 构造函数,所以 Python 不会自动调用基类的构造函数,您必须自己显式地调用它。

#多重继承
class sample(speaker,student):
    a =''
    def __init__(self,n,a,w,g,t):
        student.__init__(self,n,a,w,g)
        speaker.__init__(self,n,t)

命名空间及作用域

命名空间是从名称到对象的映射,大部分的命名空间都是通过Python字典实现的。

  • 内置名称:Python 语言内置的名称,比如函数名 abs、char 和异常名称 BaseException、Exception 等等。
  • 全局名称:模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。
  • 局部名称:函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。(类中定义的也是)

Python 的查找顺序为:局部的命名空间去 -> 全局命名空间 -> 内置命名空间

命名空间的生命周期:取决于对象的作用域,如果对象执行完成,则该命名空间的生命周期就结束。因此,我们无法从外部命名空间访问内部命名空间的对象。

作用域就是一个 Python 程序可以直接访问命名空间的文字区域,即作用域限制了是否可以访问到对应命名空间内的东西。在一个 python 程序中,直接访问一个变量,会从内到外依次访问所有的作用域直到找到,否则会报未定义的错误。

  • L(Local):最内层,包含局部变量,比如一个函数/方法内部。x

  • E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。

  • G(Global):当前脚本的最外层,比如当前模块的全局变量。

  • B(Built-in): 包含了内建的变量/关键字等。,最后被搜索

规则顺序: L –> E –> G –>B

[菜鸟教程](Python3 命名空间和作用域 | 菜鸟教程 (runoob.com))

注意:内层函数可以访问外层函数中定义的变量,但不能重新赋值,除非使用nolocal(闭包作用域)或global(全局作用域)

异常

python中异常没有catch,直接

try:
    operations...;
except xxError:
    operations...;
finally:
    operations...;

try中获取的资源要在finally中释放,或者使用with语句

with

with 语句使用了一个协议。它获取 open 语句返回的对象thefile。它总是在启动下面的代码块之前调用调用thefile.__enter__函数,在完成代码块之后调用thefile.__exit__函数。因此,我们在 finally 块中编写的代码应该由__exit__方法自动处理。

with open("poem.txt") as f:
    for line in f:
        print(line, end='')

其他

lambda

Lambda 语句用于创建新的函数对象。实际上,lambda 接受一个或几个参数,后面跟一个表达式,成为函数的主体,返回值就是该表达式的结果

  • 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率
  • lambda函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。
points = [{'x': 2, 'y': 3},
          {'x': 4, 'y': 1}]
points.sort(key=lambda i: i['y'])
print(points)

列表推导式

用于从现有列表生成新列表

listone = [2, 3, 4]
listtwo = [2*i for i in listone if i > 2]

装饰器

如果想要扩展一个函数的功能,并且不改变函数内部代码。很自然的,我们就会想到把这个函数以及增添功能所对应的代码包起来,然后这个新的函数就是原函数进化版。为了方便以及规范这种改造,可使用装饰器。装饰器函数的形数参是想要升级的函数,在装饰器函数中定义新函数(函数+添加的功能),并且返回新函数

def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper
@log
def now():
    print('2015-3-25')

如果装饰器还要传参数,将装饰器函数再包一层(三层)

def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator
  • 装饰器 @log 不带参数的情况下,实际上是直接调用 now = log(now)
  • 装饰器 @log 带参数的情况下,实际上是调用 now = log(text)(now)
  • 函数的__name__属性会从原函数变为装饰器函数中的wrapper函数(返回就是这个函数)。import functools,在定义wrapper()的前面加上@functools.wraps(func)即可。
  • 最内层函数一般return是因为防止内层函数return的值丢失,毕竟外面可能会用到。
posted @ 2021-03-19 17:30  暗鸦  阅读(213)  评论(0)    收藏  举报