【python】小知识总结(持续更新)

本文深入探讨了Python编程中的关键概念,包括if __name__ == '__main__'的作用、下划线在变量名中的意义、assert断言的使用、装饰器的原理与应用、@property装饰器的使用方法、__init__.py文件的功能以及super()函数的高级运用,旨在帮助读者掌握Python编程的高级技巧。

本篇文章用于我在学习python过程中遇到的小问题及参考解答,如有错误,请不吝指教。


1. if name == '__main __'有什么作用?

答:在执行一个python脚本前,python解释器会定义一些特殊的变量,__name__即是其中之一。当该脚本是从命令行中被直接执行时,__name__的值为“__main__";当该脚本文件被另外一个脚本文件import或者从命令行中被import时,__name__的值即为该脚本的名称。因此,我们可以利用如下语句:

if __name__ == "__main__":
	# some code here
	pass  # can be deleted

来选择性地执行某些代码(比如,我们不希望在另一个脚本文件中import该脚本文件时被执行的代码即可放在if体中)。

参考__name__ (A Special Variable) in Python

2. python中的下划线有什么作用?

答:下划线主要用作定义变量,但是下划线的位置对于该变量是有极大影响的。常见的几种如下:

1) 单个下划线置于变量头部,如:_var。这样的变量名常见于类中成员名,它的作用是让该变量变为“weak internal use indicator”,也就是说,当我们使用如下语句:

from moduleName import *

时,该成员不会被import。表面上起到了私有的效果,不过并非真正的私有,我们仍能通过moduleName._var 进行访问。

2)双下划线置于变量头部,如__var。双下划线也常用于类中成员名,所起的作用是:mangling,即解释器会将该成员的名称解释为:_ClassName__var,从而即使是采用ClassName.__var也不能访问到该变量,从而看起来更像私有变量(因为python本身目前并不支持truly private,该变量仍能通过某些方式进行访问)。

3)变量头尾各有双下划线。这样定义的变量通常具有特殊含义,比如我们在定义类时要定义__init__()方法,该方法在创建对象时会被自动调用。

4)当在命令行中进行交互时,单个下划线也被用来保存最后一个表达式的值,比如:

>>> 10
10
>>> _
10
>>> "Hello"
'Hello'
>>> _
'Hello'

参考Understanding the underscore( _ ) of Python

3. 在python中如何使用assert?

同其他编程语言如C++一样,python中也有assert;不过同C++中的assert()函数不同的是,python中的assert是一个关键字。具体使用方法如下:

assert Expression[, Arguments]

Expression中的内容为False时,会报AssertionError,如果使用了Arguments的话,同时还会打印Arguments的内容。示例:

>>> assert 3>4, "Some error!"
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
AssertionError: Some error!

注意不能用括号将ExpressionArguments括起来,否则Python解释器会将它们看成一个参数。

如果想要在运行脚本时忽略这些assert,可以使用如下语句:

python -O script.py

参考
assertions in python
what is the use of assert in python

4. 简要介绍Python装饰器

python的装饰器(decorator)可以在不改变原函数定义的前提下,增加函数的功能。
使用示例:

def fun1(func):
	print("func1 excuted!")
	return func

@fun1
def fun2():
	print("func2 excuted!")

fun2()

上面的代码的执行结果为:

func1 excuted!
func2 excuted!

可以看到,在没有改变func2()自身的定义下,我们对func2()的功能进行了扩充。

参考装饰器

5. @property怎么使用?

如4所述,@property是python内置的装饰器,其作用是:把一个方法变成属性调用,尤其常见于对某个变量的getter和setter进行装饰。

我们知道,面向对象编程通常要求我们对外隐藏对象的成员变量,因此,为了对某个对象的成员变量进行访问或修改,我们通常需要新增getter和setter方法。

增加getter或setter的不方便之处在于:1)我们对该变量的访问或修改变得不是那么自然了;2)如果在新增之前,代码已经对该变量进行了访问,那么我们需要把它们都进行修改,换句话讲,代码丧失了向后兼容性。

@property可以很好的解决上述问题,其函数签名为:

property(fget=None, fset=None, fdel=None, doc=None)

那么@property是如何使用的呢?看下面的示例:

class Celsius:
    def __init__(self, temperature = 0):
        self.__temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

    @property
    def temperature(self):  #getter
        print("Getting value")
        return self.__temperature

    @temperature.setter
    def temperature(self, value): #setter
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        print("Setting value")
        self.__temperature = value

ce = Celsius()
print(ce.temperature)
ce.temperature = 25
print(ce.temperature)

上述代码的执行结果为:

Getting value
0
Setting value
Getting value
25

可以看到:当我们直接访问ce.temperature时,实际上会调用如下函数:

@property
def temperature(self):  #getter
    print("Getting value")
    return self.__temperature

当我们对ce.temperature进行赋值时,实际上会调用如下函数:

@temperature.setter
def temperature(self, value): #setter
    if value < -273:
        raise ValueError("Temperature below -273 is not possible")
    print("Setting value")
    self.__temperature = value

可以看到,使用@property我们达到了如下效果:1)隐藏了真正的私有变量__temperature;2)对于该变量的访问和修改就好像它不是私有变量一样自然;3)如果我们不想让该变量具有写属性,直接不添加setter方法即可,从而对该变量进行了保护

参考
Python @property
使用@property

6. 如何使用__init__.py文件?

__init__.py常见于python包。当我们试图import一个包时,该包内的__init__.py文件便会被执行,因此,我们可以在__init__.py里自定义当该包被import时,哪些部分会被import。

先考虑包内没有子包的情况,假设我们当前的包的目录结构如下:

code_base
│  m1.py
│  m2.py
│  __init__.py

其中m1.py和m2.py的内容如下:

# m1
def func1():
	print("This is func1")

# m2
def func2():
    print("This is func2")

1)如果__init__()的内容如下:

import code_base.m1

那么当我们在code_base的根目录下执行import code_base时,实际上被导入的只有m1,而没有m2,验证如下:

>>> code_base.m2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'code_base' has no attribute 'm2'

2)如果__init__()的内容如下:

__all__ = ['m1','m2']

并执行:from code_base import * 后,结果如何呢?可以验证,此时m1和m2都被import了。当然,这是__all__的功劳:当我们在执行from code_base import * 时,python解释器会将__all__内列出的模块导入。

3)如果__init__()的内容如下:

#  __all__ = ['m1','m2']
import code_base.m1

并执行:from code_base import *。这时,由于我们没有显示地给__all__赋值,因此,最终的效果跟import code_base.m1一样。

参考
what is __init__.py for
Python __init__.py作用详解

7. 如何使用带参数的super()?

面向对象编程不可避免的话题就是继承(inheritance)。我们知道,在C++中,实例化子类前会调用父类的构造函数;而在python里,为了达到相同的目的,我们会使用super()。这里我们考虑单继承带参数的super()。

看下面这个例子:

class Rectangle:
    def __init__(self, length, width):
        self.length = length
        self.width = width

    def area(self):
        return self.length * self.width

    def perimeter(self):
        return 2 * self.length + 2 * self.width

class Square(Rectangle):
    def __init__(self, length):
        super(Square, self).__init__(length, length)

从上面的例子中我们可以看到,super()可以接受两个参数,第一个参数是子类,第二个参数是该子类的instance对象,上面例子中的调用结果是:调用该子类父类的__init__()。

如果我们再添加一个类:

class Cube(Square):
    def surface_area(self):
        face_area = super(Square, self).area()
        return face_area * 6

    def volume(self):
        face_area = super(Square, self).area()
        return face_area * self.length

那么结果会怎么样呢?答案是会调用Rectangle()类的area()方法。因为如前所述,这里Square是子类,而self指代Cube的实例对象,自然也是Square的实例对象,因此这里的super(Square, self).area()会去查找Square的父类中的area()方法进行调用。

参考Superchange Your Classes with Python super()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值