本篇文章用于我在学习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!
注意不能用括号将Expression和Arguments括起来,否则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()方法进行调用。
本文深入探讨了Python编程中的关键概念,包括if __name__ == '__main__'的作用、下划线在变量名中的意义、assert断言的使用、装饰器的原理与应用、@property装饰器的使用方法、__init__.py文件的功能以及super()函数的高级运用,旨在帮助读者掌握Python编程的高级技巧。
1万+

被折叠的 条评论
为什么被折叠?



