2024-09-01
Python
00

目录

1 作用域问题(当你对作用域中的变量进行赋值时,该变量将成为该作用域的局部变量,并在外部作用域中隐藏任何类似命名的变量)
2 如何跨模块共享全局变量(规范方法是创建一个特殊模块)
3 好习惯:函数不使用可变对象作为默认值,除非用于缓存结果值技巧
4 使用函数参数列表中的 和 说明符收集参数;这会将位置参数作为元组,将关键字参数作为字典。
5 深拷贝浅拷贝与可变不可变对象之间的事情
6 编写带输出参数的函数
7 使用嵌套作用域,或者使用可调用对象创建高阶函数
8 使用dir()可以获得类的属性和方法
9 逗号在 Python 中不是运算符,"a" in "b", "a"是("a" in "b"), "a"
10 三目 “?:” small = x if x < y else y
11 函数参数列表中的斜杠(/)是什么意思?函数参数列表中的斜杠表示在它之前的形参是仅限位置形参。
12 十六进制和八进制整数表示 0x22 0o22
13 字符串转换为数字用eval()不安全
13 如何修改字符串?
14 如何使用字符串调用函数/方法?
15 将多个字符串连接在一起的最有效方法是什么?
16 如何以相反的顺序迭代序列?
17 如何从列表中删除重复项?
18 如何创建多维列表?
19 如何按其他列表中的值对一个列表进行排序?
20 实例方法、静态方法和类方法区别及其应用场景

python编程常见问题:

https://docs.python.org/zh-cn/3/faq/programming.html#programming-faq

本文想对此做一些摘要。

1 作用域问题(当你对作用域中的变量进行赋值时,该变量将成为该作用域的局部变量,并在外部作用域中隐藏任何类似命名的变量)

当你对作用域中的变量进行赋值时,该变量将成为该作用域的局部变量,并在外部作用域中隐藏任何类似命名的变量。

解决:

使用 global 、nonlocal 去显式声明变量的作用域。

代码1

python
a=1 def func(): a=2 print(a) func() print(a)

代码1执行结果:

python
2 1

代码2

python
a=1 def func(): print(a) a=2 func() print(a)

代码2执行结果出现错误:

python
UnboundLocalError: local variable 'a' referenced before assignment

https://www.runoob.com/python/python-functions.html

匿名函数

python 使用 lambda 来创建匿名函数。

lambda只是一个表达式,函数体比def简单很多。

lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。

lambda函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。

虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。

语法

lambda函数的语法只包含一个语句,如下:

lambda [arg1 [,arg2,.....argn]]

2 如何跨模块共享全局变量(规范方法是创建一个特殊模块)

在单个程序中跨模块共享信息的规范方法是创建一个特殊模块(通常称为config或cfg)。只需在应用程序的所有模块中导入配置模块;然后该模块可用作全局名称。因为每个模块只有一个实例,所以对模块对象所做的任何更改都会在任何地方反映出来。 例如:

config.py:

python
x = 0 # Default value of the 'x' configuration setting

mod.py:

python
import config config.x = 1

main.py:

python
import config import mod print(config.x)

请注意,出于同样的原因,使用模块也是实现Singleton设计模式的基础。

3 好习惯:函数不使用可变对象作为默认值,除非用于缓存结果值技巧

这种类型的缺陷通常会惹恼新手程序员。考虑这个函数

python
def foo(mydict={}): # Danger: shared reference to one dict for all calls ... compute something ... mydict[key] = value return mydict

第一次调用此函数时,mydict 包含一项。第二次,mydict 包含两项,因为当 foo() 开始执行时, mydict 中已经有一项了。

函数调用经常被期望为默认值创建新的对象。 但实际情况并非如此。 默认值会在函数定义时一次性地创建。 如果对象发生改变,就如本示例中的字典那样,则对函数的后续调用将会引用这个被改变的对象。

按照定义,不可变对象例如数字、字符串、元组和 None 因为不可变所以是安全的。 对可变对象例如字典、列表和类实例的改变则可能造成迷惑。

由于这一特性,在编程中应遵循的一项好习惯是不使用可变对象作为默认值。 而应使用 None 作为默认值和函数中的值,检查值为 None 的形参并创建相应的列表、字典或其他可变对象。 例如,不要这样写:

python
def foo(mydict={}): ...

而要这样写:

python
def foo(mydict=None): if mydict is None: mydict = {} # create a new dict for local namespace

这一特性有时会很有用处。 当你有一个需要进行大量耗时计算的函数时,一个常见技巧是将每次调用函数的参数和结果值缓存起来,并在同样的值被再次请求时返回缓存的值。 这称为“记忆”,具体实现方式可以是这样的:

python
# Callers can only provide two parameters and optionally pass _cache by keyword def expensive(arg1, arg2, *, _cache={}): if (arg1, arg2) in _cache: return _cache[(arg1, arg2)] # Calculate the value result = ... expensive computation ... _cache[(arg1, arg2)] = result # Store result in the cache return result

你也可以使用包含一个字典的全局变量而不使用参数默认值;这完全取决于个人偏好。

4 使用函数参数列表中的 * 和 ** 说明符收集参数;这会将位置参数作为元组,将关键字参数作为字典。

5 深拷贝浅拷贝与可变不可变对象之间的事情

下面的x和y指向同一不可变对象,执行 y = x 并不会为列表创建一个副本,只是一个引用。

python
x=5 y=x

下面这代码的操作的x和y就是指向不同对象的,因为x=x+1这句话分两步,第一步计算x+1得出的6是一可变对象,只能创建新的对象出来。

python
x=5 y=x x=x+1

某些操作 (例如 y.append(10) 和 y.sort()) 是改变原对象,而看上去相似的另一些操作 (例如 y = y + [10] 和 sorted(y)) 则是创建新对象。

通常在 Python 中 (以及在标准库的所有代码中) 会改变原对象的方法将返回 None 以帮助避免混淆这两种不同类型的操作。

因此如果你错误地使用了 y.sort() 并期望它将返回一个经过排序的 y 的副本,你得到的结果将会是 None,这将导致你的程序产生一个容易诊断的错误。

但是,还存在一类操作,不同的类型执行相同的操作会有不同的行为:那就是增强赋值运算符。 例如,+= 会原地改变列表,但不会改变元组或整数 (a_list += [1, 2, 3] 与 a_list.extend([1, 2, 3]) 一样都会改变 a_list,而 some_tuple += (1, 2, 3) 和 some_int += 1 则会创建新的对象)。

换而言之:

如果我们有一个可变对象 (list, dict, set 等等),我们可以使用某些特定的操作来改变它,所有指向它的变量都会显示它的改变。

如果我们有一个不可变对象 (str, int, tuple 等等),所有指向它的变量都将显示相同样的值,但凡是会改变这个值的操作将总是返回一个新对象。

如果你想知道两个变量是否指向相同的对象,你可以使用 is 运算符,或内置函数 id()。

python
import copy a=[1,2,3,4] b=copy.copy(a) b[0]=0 print(id(a)==id(b)) # 引用 a=[1,2,3,4] b=a b[0]=0 print(id(a)==id(b)) a=[1,2,3,4] b=a[:] b[0]=0 print(id(a)==id(b)) a=[1,2,3,4] b=a[0:3] b[0]=0 print(id(a)==id(b)) a={"d":12,"r":23} b=copy.copy(a) b["d"]=0 print(id(a)==id(b)) a={"d":12,"r":23} b=a.copy() b["d"]=0 print(id(a)==id(b)) # 引用 a={"d":12,"r":23} b=a b["d"]=0 print(id(a)==id(b))

结果

python
False True False False False False True

6 编写带输出参数的函数

通过返回一个结果元组:

python
def func2(a, b): a = 'new-value' # a and b are local names b = b + 1 # assigned to new objects return a, b # return new values x, y = 'old-value', 99 x, y = func2(x, y) print(x, y) # output: new-value 100

这几乎总是最清晰明了的解决方案。

7 使用嵌套作用域,或者使用可调用对象创建高阶函数

使用嵌套作用域

python
def linear(a, b): def result(x): return a * x + b return result print(linear(1,2)(3))

使用可调用对象

python
class linear: def __init__(self, a, b): self.a, self.b = a, b def __call__(self, x): return self.a * x + self.b print(linear(1,2)(3))

可调用对象方式的缺点是速度略慢且生成的代码略长。 但是,请注意一组可调用对象能够通过继承来共享签名:

python
class linear: def __init__(self, a, b): self.a, self.b = a, b def __call__(self, x): return self.a * x + self.b class exponential(linear): # __init__ inherited def __call__(self, x): return self.a * (x ** self.b) print(exponential(1,2)(2))

对象可以封装多个方法的状态:

python
class counter: value = 0 def set(self, x): self.value = x def up(self): self.value = self.value + 1 def down(self): self.value = self.value - 1 count = counter() inc, dec, reset = count.up, count.down, count.set

8 使用dir()可以获得类的属性和方法

python
class counter: value = 0 def set(self, x): self.value = x def up(self): self.value = self.value + 1 def down(self): self.value = self.value - 1 print(dir(counter))

返回

python
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'down', 'set', 'up', 'value']

9 逗号在 Python 中不是运算符,"a" in "b", "a"是("a" in "b"), "a"

10 三目 “?:” small = x if x < y else y

11 函数参数列表中的斜杠(/)是什么意思?函数参数列表中的斜杠表示在它之前的形参是仅限位置形参。

在形参列表末尾的斜杠意味着两个形参都是仅限位置形参。 因此,附带关键字参数调用 divmod() 将会导致报错:

python
>>> >>> divmod(x=3, y=4) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: divmod() takes no keyword arguments

12 十六进制和八进制整数表示 0x22 0o22

13 字符串转换为数字用eval()不安全

用自带的int() float(),eval()太慢,而且eval()能执行python表达式不安全。

13 如何修改字符串?

无法修改,因为字符串是不可变对象。 在大多数情况下,你应该使用你想要的各种部分来构造一个新字符串。 但是,如果你想要一个可以原地修改 Unicode 数据的对象,可尝试使用 io.StringIO 对象或 array 模块:

python
>>> >>> import io >>> s = "Hello, world" >>> sio = io.StringIO(s) >>> sio.getvalue() 'Hello, world' >>> sio.seek(7) 7 >>> sio.write("there!") 6 >>> sio.getvalue() 'Hello, there!' >>> import array >>> a = array.array('u', s) >>> print(a) array('u', 'Hello, world') >>> a[0] = 'y' >>> print(a) array('u', 'yello, world') >>> a.tounicode() 'yello, world'

14 如何使用字符串调用函数/方法?

有多种技巧可供选择。

最好的做法是使用一个将字符串映射到函数的字典。 这一技巧的主要优势在于字符串不必与函数名称一致。 这也是用于模拟其他语言中 case 结构的主要技巧:

python
def a(): pass def b(): pass dispatch = {'go': a, 'stop': b} # Note lack of parens for funcs dispatch[get_input()]() # Note trailing parens to call function

使用内置函数 getattr()

python
import foo getattr(foo, 'bar')() 请注意 getattr() 可用于任何对象,包括类、类实例、模块等等。 在标准库中多次使用了这个技巧,例如: class Foo: def do_foo(self): ... def do_bar(self): ... f = getattr(foo_instance, 'do_' + opname) f()

使用 locals() 或 eval() 来解析出函数名:

python
def myFunc(): print("hello") fname = "myFunc" f = locals()[fname] f() f = eval(fname) f()

注意:使用 eval() 速度慢而且危险。 如果你不能绝对掌控字符串的内容,别人将能传入可被解析为任意函数直接执行的字符串。

15 将多个字符串连接在一起的最有效方法是什么?

str 和 bytes 对象是不可变的,因此将多个字符串连接在一起效率很低,因为每个连接都会创建一个新对象。在一般情况下,总运行时间是总字符串长度的二次方。

要连接多个 str 对象,通常推荐的用法是将它们放入一个列表中并在结尾处调用 str.join() :

python
my_strings="abcdefg" chunks = [] for s in my_strings: chunks.append(s) print(chunks) result = ''.join(chunks) print(result)

16 如何以相反的顺序迭代序列?

使用 reversed() 内置函数,这是Python 2.4中的新功能:

python
for x in reversed(sequence): ... # do something with x ...

这不会修改您的原始序列,而是构建一个反向顺序的新副本以进行迭代。

在 Python 2.3 里,您可以使用扩展切片语法:

python
for x in sequence[::-1]: ... # do something with x ...

17 如何从列表中删除重复项?

有关执行此操作的许多方法的详细讨论,请参阅 Python Cookbook:

https://code.activestate.com/recipes/52560/

如果您不介意重新排序列表,请对其进行排序,然后从列表末尾进行扫描,删除重复项:

python
if mylist: mylist.sort() last = mylist[-1] for i in range(len(mylist)-2, -1, -1): if last == mylist[i]: del mylist[i] else: last = mylist[i]

如果列表的所有元素都可以用作设置键(即:它们都是 hashable ),这通常会更快:

python
mylist = list(set(mylist))

这会将列表转换为集合,从而删除重复项,然后返回到列表中。

如果序列时不可哈希的,想要去除重复项,需要对上述代码稍作修改:

python
# example2.py # # Remove duplicate entries from a sequence while keeping order def dedupe(items, key=None): seen = set() for item in items: val = item if key is None else key(item) if val not in seen: yield item seen.add(val) if __name__ == '__main__': a = [ {'x': 2, 'y': 3}, {'x': 1, 'y': 4}, {'x': 2, 'y': 3}, {'x': 2, 'y': 3}, {'x': 10, 'y': 15} ] print(a) print(list(dedupe(a, key=lambda a: (a['x'],a['y']))))

18 如何创建多维列表?

可以使用列表推导式:

python
w, h = 2, 3 A = [[None] * w for i in range(h)]

19 如何按其他列表中的值对一个列表进行排序?

将它们合并到元组的迭代器中,对结果列表进行排序,然后选择所需的元素。

python
>>> >>> list1 = ["what", "I'm", "sorting", "by"] >>> list2 = ["something", "else", "to", "sort"] >>> pairs = zip(list1, list2) >>> pairs = sorted(pairs) >>> pairs [("I'm", 'else'), ('by', 'sort'), ('sorting', 'to'), ('what', 'something')] >>> result = [x[1] for x in pairs] >>> result ['else', 'sort', 'to', 'something']

最后一步的替代方案是:

python
>>> >>> result = [] >>> for p in pairs: result.append(p[1])

如果你觉得这个更容易读懂,那么你可能更喜欢使用这个而不是前面的列表推导。然而,对于长列表来说,它的速度几乎是原来的两倍。为什么?首先, append() 操作必须重新分配内存,虽然它使用了一些技巧来避免每次都这样做,但它仍然偶尔需要这样做,而且代价相当高。第二,表达式 "result.append" 需要额外的属性查找。第三,必须执行所有这些函数调用会降低速度。

20 实例方法、静态方法和类方法区别及其应用场景

https://blog.csdn.net/helloxiaozhe/article/details/79940321

python
# coding:utf-8 class Foo(object): """类三种方法语法形式""" def instance_method(self): print("是类{}的实例方法,只能被实例对象调用".format(Foo)) @staticmethod def static_method(): print("是静态方法") @classmethod def class_method(cls): print("是类方法") foo = Foo() foo.instance_method() foo.static_method() foo.class_method() print('----------------') Foo.static_method() Foo.class_method()
如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:Dong

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC。本作品采用《知识共享署名-非商业性使用 4.0 国际许可协议》进行许可。您可以在非商业用途下自由转载和修改,但必须注明出处并提供原作者链接。 许可协议。转载请注明出处!