python编程常见问题:
https://docs.python.org/zh-cn/3/faq/programming.html#programming-faq
本文想对此做一些摘要。
当你对作用域中的变量进行赋值时,该变量将成为该作用域的局部变量,并在外部作用域中隐藏任何类似命名的变量。
解决:
使用 global 、nonlocal 去显式声明变量的作用域。
代码1
pythona=1
def func():
a=2
print(a)
func()
print(a)
代码1执行结果:
python2
1
代码2
pythona=1
def func():
print(a)
a=2
func()
print(a)
代码2执行结果出现错误:
pythonUnboundLocalError: 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]]
在单个程序中跨模块共享信息的规范方法是创建一个特殊模块(通常称为config或cfg)。只需在应用程序的所有模块中导入配置模块;然后该模块可用作全局名称。因为每个模块只有一个实例,所以对模块对象所做的任何更改都会在任何地方反映出来。 例如:
config.py:
pythonx = 0 # Default value of the 'x' configuration setting
mod.py:
pythonimport config
config.x = 1
main.py:
pythonimport config
import mod
print(config.x)
请注意,出于同样的原因,使用模块也是实现Singleton设计模式的基础。
这种类型的缺陷通常会惹恼新手程序员。考虑这个函数
pythondef 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 的形参并创建相应的列表、字典或其他可变对象。 例如,不要这样写:
pythondef foo(mydict={}):
...
而要这样写:
pythondef 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
你也可以使用包含一个字典的全局变量而不使用参数默认值;这完全取决于个人偏好。
下面的x和y指向同一不可变对象,执行 y = x 并不会为列表创建一个副本,只是一个引用。
pythonx=5
y=x
下面这代码的操作的x和y就是指向不同对象的,因为x=x+1这句话分两步,第一步计算x+1得出的6是一可变对象,只能创建新的对象出来。
pythonx=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()。
pythonimport 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))
结果
pythonFalse
True
False
False
False
False
True
通过返回一个结果元组:
pythondef 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
这几乎总是最清晰明了的解决方案。
使用嵌套作用域
pythondef linear(a, b):
def result(x):
return a * x + b
return result
print(linear(1,2)(3))
使用可调用对象
pythonclass 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))
可调用对象方式的缺点是速度略慢且生成的代码略长。 但是,请注意一组可调用对象能够通过继承来共享签名:
pythonclass 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))
对象可以封装多个方法的状态:
pythonclass 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
pythonclass 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']
在形参列表末尾的斜杠意味着两个形参都是仅限位置形参。 因此,附带关键字参数调用 divmod() 将会导致报错:
python>>>
>>> divmod(x=3, y=4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: divmod() takes no keyword arguments
用自带的int() float(),eval()太慢,而且eval()能执行python表达式不安全。
无法修改,因为字符串是不可变对象。 在大多数情况下,你应该使用你想要的各种部分来构造一个新字符串。 但是,如果你想要一个可以原地修改 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'
有多种技巧可供选择。
最好的做法是使用一个将字符串映射到函数的字典。 这一技巧的主要优势在于字符串不必与函数名称一致。 这也是用于模拟其他语言中 case 结构的主要技巧:
pythondef 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()
pythonimport foo
getattr(foo, 'bar')()
请注意 getattr() 可用于任何对象,包括类、类实例、模块等等。
在标准库中多次使用了这个技巧,例如:
class Foo:
def do_foo(self):
...
def do_bar(self):
...
f = getattr(foo_instance, 'do_' + opname)
f()
使用 locals() 或 eval() 来解析出函数名:
pythondef myFunc():
print("hello")
fname = "myFunc"
f = locals()[fname]
f()
f = eval(fname)
f()
注意:使用 eval() 速度慢而且危险。 如果你不能绝对掌控字符串的内容,别人将能传入可被解析为任意函数直接执行的字符串。
str 和 bytes 对象是不可变的,因此将多个字符串连接在一起效率很低,因为每个连接都会创建一个新对象。在一般情况下,总运行时间是总字符串长度的二次方。
要连接多个 str 对象,通常推荐的用法是将它们放入一个列表中并在结尾处调用 str.join() :
pythonmy_strings="abcdefg"
chunks = []
for s in my_strings:
chunks.append(s)
print(chunks)
result = ''.join(chunks)
print(result)
使用 reversed() 内置函数,这是Python 2.4中的新功能:
pythonfor x in reversed(sequence):
... # do something with x ...
这不会修改您的原始序列,而是构建一个反向顺序的新副本以进行迭代。
在 Python 2.3 里,您可以使用扩展切片语法:
pythonfor x in sequence[::-1]:
... # do something with x ...
有关执行此操作的许多方法的详细讨论,请参阅 Python Cookbook:
https://code.activestate.com/recipes/52560/
如果您不介意重新排序列表,请对其进行排序,然后从列表末尾进行扫描,删除重复项:
pythonif 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 ),这通常会更快:
pythonmylist = 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']))))
可以使用列表推导式:
pythonw, h = 2, 3
A = [[None] * w for i in range(h)]
将它们合并到元组的迭代器中,对结果列表进行排序,然后选择所需的元素。
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" 需要额外的属性查找。第三,必须执行所有这些函数调用会降低速度。
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()
本文作者:Dong
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC。本作品采用《知识共享署名-非商业性使用 4.0 国际许可协议》进行许可。您可以在非商业用途下自由转载和修改,但必须注明出处并提供原作者链接。 许可协议。转载请注明出处!