python特性笔记

下面是python特性笔记,废话不多说了,加油!!!!

python中的断言

Python中的断言是一种调试工具,用来测试某个断言条件,若断言条件为真,继续继续正常执行,若条件为假,则引发AssertionError异常并显示相关错误消息

#商品打折代码
def apply_discount(product, discount):
price = int(product['price'] * (1.0 - discount))
assert 0 <= price <= product['price']
return price
#assert断言,保证了价格大于0,并且小于原本商品的价格
def discount_pro(product,discount):
    price=int(product['price']*(1-discount))
    assert 0 < price < product['price']
    print(price)
    
shoes={'name':'nike','price':4000}
discount_pro(shoes,0.25)
#测试断言,尝试输入错误的逻辑,引发断言
def discount_pro(product,discount):
    price=int(product['price']*(1-discount))
    assert 0 < price < product['price']
    print(price)
    
shoes={'name':'nike','price':4000}
discount_pro(shoes,2.0)
#下面的信息显示,引发了AssertionError错误信息
'''AssertionError                            Traceback (most recent call last)
<ipython-input-2-4018e69a3d43> in <module>()
      5 
      6 shoes={'name':'nike','price':4000}
----> 7 discount_pro(shoes,2.0)
      8 
      9 

<ipython-input-2-4018e69a3d43> in discount_pro(product, discount)
      1 def discount_pro(product,discount):
      2     price=int(product['price']*(1-discount))
----> 3     assert 0 < price < product['price']
      4     print(price)
      5 

AssertionError: 
'''

断言是用于程序内部自检,例如声明代码中不可能出现的条件,若出发了,则证明程序中有bug

python的断言语句是一种调试辅助功能,不是用来处理错误的机制,可有效地帮助开发人员快速找到bug的根本原因


python的断言语法

assert语句语法:

assert_stmt ::= “assert” expression1 ["," expression2]

expression1是需要测试的条件,可选的为expression2为错误消息,若断言失败则显示此消息

if cond == 'x':
	do_x()
elif cond == 'y':
	do_y()
else:
#断言错误
assert False, (
 'This should never happen, but it does '
 'occasionally. We are currently trying to '
 'figure out why. Email dbader if you '
 'encounter this in the wild. Thanks!')
  • 注意事项1:不要使用断言验证数据

    怎么解决数据验证问题呢:

    使用if语句验证,在必要时出发验证异常

    def delete_product(product_id, user):
    	if not user.is_admin():
    	raise AuthError('Must be admin to delete')
    	if not store.has_product(product_id):
    	raise ValueError('Unknown product id')
    	store.get_product(product_id).delete()
      
    
  • 注意事项2:永不失败的断言

关键要点:
  1. python断言语句是一种测试某个条件的调试辅助功能,可作为程序的内部自检
  2. 断言应该勇于帮助开发人员识别bug,不适用于处理运行错误的机制
  3. 设置解释器可全局禁用断言

巧妙地放置逗号

在python的列表,字典,集合常量中添加或移除项时,在所有行的后面添加一个逗号

#将列表字典或集合常量分割为多行
'''在使用diff检查添加修改操作时,可以让源码控制系统看出来'''
names=[
	'Alice',
	'bob',
	'dilbert'
]

由于列表的修改,会涉及添加逗号,再修改值的过程,所以在写列表时,最好在每一行的结尾都加上逗号,甚至是最后一行

names=[
	'alice',
	'bob',
	'mike',
]
关键要点
  1. 合理的格式化及逗号能让列表,字典和集合常量容易维护
  2. python的字符串字面值拼接能带来帮助,也能引发bug

上下文管理器和with语句
'''打开文件时建议使用with语句,保证了文件描述符在程序执行离开with语句的上下文后自动关闭'''
with open('hello.txt', 'w') as f:
	f.write('hello, world!')
    
#自定义上下文管理器实现open()功能
class ManagedFile:
    def __init__(self,name):
        self.name=name
    def __enter__(self):
        self.file=open(self.name,'w')
        return self.file
    def __exit__(self,exc_type,exc_val,exc_tb):
        if self.file:
            self.file.close()
'''ManagedFile类遵循了上下文管理器协议,可以类似open()使用,支持with语句'''            
with ManagedFile('hello.txt') as f: 		f.write('hello, world!')
	f.write('bye now')
'''当执行程序进入with语句上下文时,python、调用__enter__获取资源,离开with上下文时,python调用__exit__释放资源'''

'''managed_file()是生成器,开始获取资源,之后暂停执行,并生成资源给调用者使用,当;离开with语句时,生成器继续执行剩余的清理步骤,将资源归还给系统,contextlib是python库'''
from contextlib import contextmanager
#添加装饰器
@contextmanager
def managed_file(name):
	try:
		f = open(name, 'w')
		yield f
	finally:
		f.close()
with managed_file('hello.txt') as f:
	f.write('hello, world!')
	f.write('bye now')
关键要点
  1. with语句通过上下文管理器中封装try。。。finally的标准用法来简化异常处理
  2. with语句一般用来管理系月资源的安全释放与获取,资源首先由with语句获取,并执行离开with上下文时释放
  3. 有效的利用with有助于避免资源泄露的我呢提,让代码易读

下划线,双下划线及其他
  • 前置单下划线:_var:涉及变量名和方法名时,前置单下划线只有约定含义,是一种提示,并不会影响程序本身的行为,前置下划线会影响从模块中导入名称的方式
# my_module.py:
def external_func():
return 23
def _internal_func():
return 42
'''如果使用通配符导入的话,python不会导入这个模块中所有的名称,不会导入带有前置下划线的名称,除非在模块中定义了__all__列表覆盖行为'''
>>> from my_module import *
>>> external_func()
23
>>> _internal_func()
NameError: "name '_internal_func' is not defined"
#出错,无法是被该函数,应为带有前置下划线


应该避免通配符导入,会导致名称空间的混淆,最好使用常规导入方法,不会受到前置单下划线的命名约定影响


  • 后置单下划线:var_:变量名可能是python中的关键词,这是使用后置单下划线可以避免逻辑冲突
def make_object(name, class):
	SyntaxError: "invalid syntax"
	def make_object(name, class_):
	pass
  • 前置双下划线:__var:双下划线可重写属性名称,避免子类的命名冲突,成为名称改写(name mangling)
class Test():
    def __init__(self):
        self.foo=11
        self._bar=23
        self.__baz=42
        
class ExtendedTest(Test):
    def __init__(self):
        super.__init__()
        self.foo=220
        self._bar=330
        self.__baz=1
 '''此时查看ExtendedTest时,会发现,foo,和_bar变量被修改了,但是__baz变量没有被修改,为了防止以外的变动,__baz变成了_ExtendedTest__baz,但是_Test__baz依然存在,相当于定义了两个不同的变量属性
 '''
class ManglingTest:
def __init__(self):
	self.__mangled = 'hello'
def get_mangled(self):
	return self.__mangled
ManglingTest().get_mangled()
	'hello'
ManglingTest().__mangled
AttributeError:
"'ManglingTest' object has no attribute '__mangled'"
#----------------------------------------------------------------------
_Var__ible=42
'''此处定义了全局变量,在使用时__ible被扩展成_Var__ible,调用时,变成调用了全局变量'''
class Var():
    def test(self):
        return __ible

print(Var().test())
  • 前后双下划线:var

双下划线称为魔法方法,最好在命名时避免以双下划线开头与结束

  • 单下划线:_

单下划线有时用作名称,来表示变量时临时的或无关紧要的,单下划线知识一个有效的变量名,有时用作占位符

color=('red','yellow','green','pink')
a,_,_,d=color
print(a)
print(d)

除了用作临时变量外,大多数表示解释器计算的上一个表达式的结果

#实时构建对象
list()
_.append(1)
_.append(2)

关键要点
  1. 前置单下划线_var:命名约定,用来表示该名称仅在内部使用,一般对python解释器没有特殊含义(通配符导入除外)只作为程序员的提示
  2. 后置单下划线:命名约定,用于避免与python关键字发生冲突
  3. 前置双下划线:在环境中使用时会引发名称改写,对python解释器有特殊含义
  4. 前后双下划线:表示python语言定义的特殊方法
  5. 单下划线:有时用作临时或无意义变量名称,还可以表示上一个会话的结果

字符串格式化

新式格式化在字符串对象上调用format()函数

name='bob'
age=18
print("my name is {name},my age is {age:x}".format(name='bob',age=18))

python3.6增加了格式化字符串字面值的新式格式化方法

name='bob'
age=18
print(f"my name is {name}")

python还有一种方法叫做模板字符串(template string)

此方法不太常用,涉及到用户输入格式化字符串时,设计安全问题可使用

如何选择字符串格式化方法

  1. 如果格式字符串时用户提供的,使用模板字符串避免安全问题
  2. 考虑python版本,3.6+使用字符串字面值插入值,老版本使用新是字符串格式化方法即可
关键要点
  1. python格式化字符串不止一种方法
  2. 选择时取决具体情况
  3. 难以选择时,看上方总结

python之禅
import this
'''The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those'''
'''美丽好过丑陋,
浅显好过隐晦,
简单好过复合,
复合好过复杂,
扁平好过嵌套,
稀疏好过密集,
可读性最重要,
即使祭出实用性为理由,特例也不可违背这些规则。
不应默认包容所有错误,得由人明确地让它闭嘴!
面对太多的可能,不要尝试猜测,应该有一个(而且是唯一)直白
的解决方法。
当然,找到这个方法不是件容易的事,谁叫你不是荷兰人呢!
但是,现在就做永远比不做要好。
若实现方案很难解释,那么它就不是一个好方案;反之也成立!
名称空间是个绝妙想法——现在就来共同体验和增进这些吧'''

函数
def yell(context):
    return context.upper()+"!"
yell("hello")

由于yell函数是python的一个对象,因此可以分配给另一个变量

bark=yell

即便删除yell函数,bark照样运行

del yell
bark("wolf")

python在创建函数时,为每一个函数附加一个用于调试的字符串标识符,使用__name__属性可以访问这个内部标识符

def yell(context):
   return context.upper()+"!"
yell("hello")

bark=yell
print(bark.__name__)

返回的结果为yell

指向函数的变量和函数本身是彼此独立的


函数存放在数据结构中
funcs=[yell,str.upper,str.capitalize]
for f in funcs:
    print(f,f("hello world"))
'''<function yell at 0x000001F51107DC80> HELLO WORLD!
<method 'upper' of 'str' objects> HELLO WORLD
<method 'capitalize' of 'str' objects> Hello world'''

存储在列表中的函数对象可以直接调用,无需实现分配变量


函数可传递给其他函数
def greeting(function):
    words=function("hellO")
    return words

greeting(yell)

能够接受函数作为参数的函数叫做高阶函数

map函数时python内置高阶函数,map接受一个函数对象和一个可迭代对象,然后在每个可迭代对象中的每个元素上调用该函数

list(map(yell,['hello','dog']))

函数可以嵌套
def speak(contents):
    def low(t):
        return t.upper()+"!"
    return low(contents)

speak('hello')

def get_speak_func(num):
    def low(contents):
        print(contents.lower()+"!")
        
    def up(contents):
        print(contents.upper()+"!")
        
    if num > 1:
        return low
    if num <= 1 :
        return up
    
    
get_speak_func(20)
get_speak_func(0.5)
'''返回的函数可直接调用,也可以指定一个变量再使用'''

函数可捕捉局部状态
def get_speak_func(text, volume):
def whisper():
return text.lower() + '...'
def yell():
return text.upper() + '!'
if volume > 0.5:
return yell
else:
return whisper

yell与whisper函数并没有参数,但是还可以访问外部函数的参数,捕捉并记住了参数的值

拥有这种行为的函数称为词法闭包(lexical closure),简称闭包

def add_func(x1):
    def inner_func(x2):
        return x1+x2
    return inner_func

p1=add_func(2)
p1(2)

add_func作为工厂函数来创建和配置各种inner_func,内部函数可访问外部函数种位于封闭作用域的参数


对象可作为函数使用
class Adder():
    def __init__(self,x1):
        self.x1=x1
    def __call__(self,x2):
        return self.x1+x2
    
add1=Adder(2)
add1(2)

如果一个对象可调用,意味着可使用圆括号调用语法,可传参数,在类种都是由__call__双下划线方法实现的, 像函数一样调用一个对象实例实际上是尝试执行该对象的__call__方法


关键要点
  1. python中的一切皆为对象,函数也不例外,可将函数分配给变量或存储在数据结构中,作为头灯对象,函数可以传递给其他函数或作为其他函数的返回值
  2. 头等函数的特性可以用来抽象并传递程序中的行为
  3. 对象可以被设置为可调用的,可将其视为函数对待

lambda单表达式函数

语法 lambda 参数 :表达式

a=lambda x,y : x+y 
a(2,3)
(lambda c,d : c**d)(2,3)

lambda函数不必将函数对象与名称绑定,只需在lambda中创建一个想要执行的表达式,像普通函数一样调用即可

lambda函数执行时会计算其中的表达式,然后自动返回表达式的结果,所以其中有一个隐式的返回表达式


关键要点
  1. lambda函数是单表达式函数,不必与名称绑定(匿名函数)
  2. lambda函数不能使用普通的python语句,其中lambda函数总是隐式的包含一个return语句
  3. 使用lambda函数时不要过于频繁的使用,避免代码的繁琐性

装饰器

python装饰器可临时扩展或修改可调用对象的行为,同事不会永久修改可调用对象本身

装饰器可定义可重用的代码块,改变或扩展其他函数行为,函数的行为只有在装饰后才会改变

在函数定义之前放上@语法糖等于先定义函数,再运行这个装饰器

使用@语法时,会在定义是立即修饰函数

#装饰器函数
def up(func):
   def inner_func():
       old=func()
       new=old.upper()
       print("inner_func running.....")
       return new
   return inner_func


@up 
#被装饰函数
def strIng():
   return "hello"


strIng()

调研被修饰函数时会被装饰函数中的新函数所包装,进而改变被修饰函数的行为,而不必再被修饰函数中添加任何功能

装饰器通过闭包来修改可调用对象的行为,因此无需永久性的修改原对象,元可调用对象的行为仅在装饰时才会改变

将多个装饰器应用于一个函数
#装饰器函数
def strong(func):
    def wrapper():
        return "<b>"+func()+"<b>"
    return wrapper

def emp(func):
    def wrapper():
        return "<em>"+func()+"<em>"
    return wrapper

@strong
@emp
def greet():
    return "hello"


greet()
装饰含参函数
def proxy(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
  • wrapper闭包中定义使用了*和**操作符受极所有位置参数和关键字参数,并存储在args与kwargs中
  • 然后wrapper闭包使用*和**参数解包操作符将收集的的参数转发到原输入函数(被修饰函数中)
def trace(func):
    def wrapper(*args,**kwargs):
        print(f'Tracing:Calling {func.__name__}()'
             f'with {args},{kwargs}')
        original_result=func(*args,**kwargs)
        
        
        print(f'Tracing :{func.__name__}() is working '
             f'Tracing returned {original_result}')
        
    return wrapper

@trace
def say(name,word):
    return f'{name} is saying {word}'

say("Namcy","HEllo")

编写可调式的装饰器

import functools

使用functools.wrap(被装饰函数)的方法可将原函数中的文档字符串和其他元数据复制到装饰器中

必须在装饰器函数的新函数之前使用@functools.wraps(func)的方式定义

#追踪函数参数的装饰器
import functools
def trace(func):
    @functools.wraps(func)
    def wrapper(*args,**kwargs):
        print(f'Tracing:Calling {func.__name__}()'
              f'with {args},{kwargs}')
        
        original_result=func(*args,**kwargs)
        
        print(f'Tracing :{func.__name__}() is working '
             f'Tracing returned {original_result}')
    return wrapper

@trace
def say(name,word):
    return f'{name} is saying {word}'

say("Namcy","HEllo")
print(say.__name__)
关键要点
  1. 装饰器定义可重用的组件,可以将其应用于可调用对象修改其行为,同时无需永久修改被修饰函数本身的行为
  2. @语法在输入函数上调用装饰器的简写,在单个函数上使用时,注意装饰器的顺序
  3. 为了方便调试,在自己的装饰器中使用functools.wraps将被装饰对象中的元数据转移到装饰后的对象中

*args和**kwargs

非常重要的一点是args和kwargs知识一个标识而已,换成其他名称也是可以

关键要点
  1. *args和**kwargs用于在python中编写变长参数的函数
  2. *args收集额外的位置参数组成元组,**kwargs收集额外的关键字参数组成字典
  3. 实际起作用的语法是*和**,*args和kwargs只是一种约定俗成

函数参数解包
lst=[1,2,3]
tup=(4,5,6)
dct={'x':1,'y':5,'z':9}


def print_thredim(x,y,z):
    print("<%s,%s,%s>"%(x,y,z))
    
print_thredim(1,2,3)
print_thredim(*lst)
print_thredim(*tup)
print_thredim(**dct)

参数解包,使得在数据结构中的元素称为一个独立的位置参数,传递到函数之中

关键要点
  1. 两个星号操作符可用于从序列和字典中解包函数参数
  2. 高效的使用参数解包有助于为模块和函数编写更灵活的接口

返回空值

python在所有函数的末尾添加了return None语句,即如果没有指定函数返回值,则返回None

关键要点
  1. 如果函数没有指定返回值,则返回None,否则明确的返回None是分格问题
  2. 返回值是python的核心功能,显式的return None语句能更清晰的表达代码的意图

is和==的区别
  • 当两个变量指向同一个变量时,is表达式的结果是True
  • 当各变量指向的对象含有相同的内容时,==表达式的结果为True

字符串转换
class Car():
    def __init__(self,color,brand):
        self.color=color
        self.brand=brand
        
    def __str__(self):
        return f'a {self.color} car with brand is {self.brand}'
    
    
car=Car('red','baishijie')
print(car)

字符串函数__str__方法相当于print功能


python解释器会话中查看对象得到的是对象的__repr__结果

import datetime

today=datetime.datetime.today()
print(today)

str(today)
repr(today)

repr函数更多是帮助开发人员调试程序


自定义异常类
  • 自定义的异常类一般继承Exception或是某个具体的异常
def validate(name):
    if len(name) < 10:
        raise NameTooShortError(name)

class BaseValidationError(ValueError):
    pass
class NameTooShortError(BaseValidationError):
    pass
class NameTooLongError(BaseValidationError):
    pass
class NameTooCuteError(BaseValidationError):
    pass

try:
    validate(name)
except BaseValidationError as err:
    handle_validation_error(err)
    

自定义异常类结构化异常结构可以优化代码问题的解决


=关键要点
  1. 定义自己的异常类型让代码清晰表达自己的意图,易于调试
  2. 要从【python内置的Exception类或特定的异常类派生处自定义异常
  3. 可以使用继承来根据逻辑对异常分组,组成层次结构

克隆对象
  • 浅复制:是指构建一个新的容器对象,然后填充原对象中子对象的引用,浅堵制只执行一层,复制过程不会递归,因此不会创建子对象的副本
  • 深复制:递归复制,首先构造一个新的容器对象,然后递归填充原始对象中的子对象的副本,遍历整个对象树,以此来创建原对象以及所有子项的完全独立副本
  1. 制作浅对象:
lst=[['x','y','z',],['a','b','c'],['w','r','t']]
n_lst=list(lst)
print(lst)
print(n_lst)

lst.append(['new element'])
print(lst)
print(n_lst)

lst[1]=['a','y','u']
print(lst)
print(n_lst)

lst[1][1]=23
print(lst)
print(n_lst)

'''显示结果如下:
[['x', 'y', 'z'], ['a', 'b', 'c'], ['w', 'r', 't']]
[['x', 'y', 'z'], ['a', 'b', 'c'], ['w', 'r', 't']]
[['x', 'y', 'z'], ['a', 'b', 'c'], ['w', 'r', 't'], ['new element']]
[['x', 'y', 'z'], ['a', 'b', 'c'], ['w', 'r', 't']]
[['x', 'y', 'z'], ['a', 23, 'c'], ['w', 'r', 't'], ['new element']]
[['x', 'y', 'z'], ['a', 23, 'c'], ['w', 'r', 't']]

'''

可以看出,浅复制只复制了原对象的一层,对于原对象的子对象并没有复制,当要获得子对象时,还是会引用原对象的子对象,当只对第一层对象添加或减少时,不会影响副本,但是对子对象添加或修改时,会影响到副本中的子对象**

深复制
import copy

lst=[['x','y','z',],['a','b','c'],['w','r','t']]
n_lst=copy.deepcopy(lst)
print(lst)
print(n_lst)

lst.append(['new element'])
print(lst)
print(n_lst)

lst[1][1]=23
print(lst)
print(n_lst)

'''显示结果如下:
[['x', 'y', 'z'], ['a', 'b', 'c'], ['w', 'r', 't']]
[['x', 'y', 'z'], ['a', 'b', 'c'], ['w', 'r', 't']]
[['x', 'y', 'z'], ['a', 'b', 'c'], ['w', 'r', 't'], ['new element']]
[['x', 'y', 'z'], ['a', 'b', 'c'], ['w', 'r', 't']]
[['x', 'y', 'z'], ['a', 23, 'c'], ['w', 'r', 't'], ['new element']]
[['x', 'y', 'z'], ['a', 'b', 'c'], ['w', 'r', 't']]

'''

深复制会复制原对象的所有子对象,递归的复制所有内容

任何对原对象的操作都不会对副本产生任何影响


  • copy模块的copy函数是浅复制,deepcopy为深复制
  • 当要浅复制时,使用工厂函数即可,无需使用copy函数

复制任意对象
import copy

class Point():
    def __init__(self,x,y):
        self.x=x
        self.y=y
    def __repr__(self):
        return(f'Point({self.x!r},{self.y!r})')
        
#p1=copy.copy(p)
#print(p)
#print(p1)


class Rectangle():
    def __init__(self,left,right):
        self.left=left
        self.right=right
        
    def __repr__(self):
        return (f'Rectangle({self.left!r},{self.right!r})')
    
rect=Rectangle(Point(2,3),Point(3,4))
n_rect=copy.copy(rect)  #浅复制对象
print(rect)
print(n_rect)
rect.left.x=23   #对原对象的子对象进行更改,结果表示副本的子对象变动,因为是浅复制
print(rect)
print(n_rect)

'''显示结果如下:
Rectangle(Point(2,3),Point(3,4))
Rectangle(Point(2,3),Point(3,4))
Rectangle(Point(23,3),Point(3,4))
Rectangle(Point(23,3),Point(3,4))'''


d_rect=copy.deepcopy(rect)  #对原对象进行深复制
print(d_rect)
print(rect)

rect.right.y=100  #改变原对象的子对象,副本中的子对象不受影响,因为是深复制
print(rect)
print(d_rect)

'''显示结果如下:
Rectangle(Point(2,3),Point(3,4))
Rectangle(Point(2,3),Point(3,4))
Rectangle(Point(2,3),Point(3,100))
Rectangle(Point(2,3),Point(3,4)'''

关键要点
  1. 创建的浅副本不会克隆原对象的子对象,因此副本和原对象并不完全独立
  2. 对象的深副本将递归克隆原对象的子对象,副本完全独立于原对象,但创建深副本的速度较慢
  3. 使用copy模块可以复制任意对象(包括自定义类)

namedtuple命名元组
from collections import namedtuple

Car=namedtuple('Car',['brand','color'])
print(Car)
my_car=Car('baoshijie','red')
print(my_car.brand)
print(my_car.color)

my_car._asdict
my_car._replace(color='yellow')
Car._make(['yellow','999'])


  • namedtuple也是不可变类型

  • 可以快速创建不可变类

  • 同样适合解包操作

  • namedtuple的第一个参数是typename,作为创建的类名称

  • 第二个参数有以下两种形式:

    • Car=namedtuple('Car',['brand','color'])
      Car=namedtuple('Car','brand color')  #此种形式当使用namedtuple时自动调用split()函数解析参数
      
  • namedtuple的一些方法:

    • _asdict方法:以字典类型返回
    • _replace方法:替换namedtuple中的参数值
    • _make方法:创建新的类
关键要点:
  1. collection.namedtuple能方便的在python中手动定义一个内存占用较少的不可变类
  2. 使用namedtuple能够按照更易理解的结构组织数据,进而简化代码
  3. namedtuple提供了一些辅助方法,虽然这些方法以单下划线开头,但是确实公共接口的一部分,可正常使用,以单下划线开头时避免与用户命名发生冲突

类变量与实例变量
  • 类变量不受特定实例变量的影响,反过来,对类变量的改变,会影响到所有实例变量的内容
  • 类变量存储在类本身中,实例变量只是存储在特定实例对象中
  • 实例变量的改变不会影响到大环境,只会作用于特定的实例对象上
class Dog():
    num_legs=4
    def __init__(self,name):
        self.name=name
        
jack=Dog("jack")
mike=Dog("Mike")

print(jack.name)
print(mike.name)
print(Dog.num_legs)

jack.num_legs=10
print(jack.num_legs)
print(mike.num_legs)
print(jack.__class__.num_legs)

类变量是针对类的,与特定的实例对象无关

类本身不可调用实例对象属性

实例变量是特定的实例的,在创建实例对象时,调用__init__函数实现,不位于类本身中

在修改特定实例对象时,若创建了与类变量同名的变量时,会覆盖类变量,调用实例对象的新的属性,但是类变量未改变

class CounterClasObj():
    _cla_counter=0
    
    def __init__(self):
        self.__class__._cla_counter+=1
    

a=CounterClasObj()
b=CounterClasObj()
c=CounterClasObj()
print(CounterClasObj._cla_counter)

注意类变量会被新创建的实例对象的同名属性所覆盖

关键要点
  1. 类变量用于类的所有实例之间共享数据,类变量属于一个类,类的所有实例共享类变量,而不是属于某一个特定的实例对象
  2. 实例变量时特定于每个实例的数据,属于单个对象实例,不与类共享实例对象属性,每个实例变量都针对特定实例单独存储一份数据
  3. 类变量可以被同名的实例变量所覆盖,会出现bug与奇怪的行为

实例方法。类方法和静态方法
class A():
    def method(self):
        print("instance method called")
        
    @classmethod
    def classmethod(cls):
        print("class method called",cls)
        
    @staticmethod
    def staticmethod():
        print("static method called")
        
  • 实例方法接受参数self,实例方法通过self可以自由访问该对象的其他属性与方法,可以修改对象的状态
  • 实例方法还可以通过self.__class__属性访问类本身,即实例方法可以修改类状态
  • 类方法接收参数cls,无法修改实例的状态
  • 静态方法,不接受self和cls参数,但可以接受其他任意数量的参数
class A():
    def method(self):
        print("instance method called")
        
    @classmethod
    def classmethod(cls):
        print("class method called",cls)
        
    @staticmethod
    def staticmethod():
        print("static method called")
        
obj=A()
obj.method()
obj.classmethod()
obj.staticmethod()

#A.method(obj)当生成实例对象时,是把实例对象的名称传递到self中,所以可以直接使用类.method(实例对象名)即可)

A.classmethod()
A.staticmethod()
#A.method()


实例对象可调用类方法

类对象不可以调用实例方法

类对象与实例对象均可调用静态方法

class Pizaa():
    def __init__(self,ingre):
        self.ingre=ingre
        
    def __repr__(self):
        return f'Pizaa({self.ingre!r})'
    
    @classmethod
    def Pizaa1(cls):
        return cls(["potatom","tomato"])
    
    @classmethod
    def Pizaa2(cls):
        return cls(["A","b"])
    
print(Pizaa.Pizaa1())
print(Pizaa.Pizaa2())

通过此种工厂函数可以创建多个类对象,通过类方法可以按需添加格外的构造函数

import math

class Pizza():
    def __init__(self,redius,ingridents):
        self.redius=redius
        self.ingridents=ingridents
        
    def __repr__(self):
        return f'Pizza({self.redius!r},{self.ingridents})'
    
    def area(self):
        return self.staticmethod(self.redius)
    
    @staticmethod
    def staticmethod(r):
        return r**2*math.pi
    
p1=Pizza(4,["pota"])
p1.area()

        

使用静态方法和类方法可以传达开发人员的意图,添加静态方法可以避免无用类,有助于测试代码,可以不创建类实例,像普通函数一样测试即可****

关键要点
  1. 实例方法需要类实例,可通过self访问实例
  2. 类方法不需要类实例,不能访问实例(self),可通过cls访问类本身
  3. 静态方法不能访问self和cls,和普通函数一样,但属于类的名称空间
  4. 静态方法和类方法能展示和贯彻开发人员的设计意图,有助于代码的维护

python中常见的数据结构
  • 字典
from collections import OrderedDict
from collections import ChainMap
from collections import defaultdict
from types import MappingProxyType

d=OrderedDict(one=1,two=2,three=3)
print(d)
print(d.keys())
print(d.items())

#——————————————————————————————————————————————————————
d1=defaultdict(list) #设置默认返回列表类型,当访问的键不存在时,返回空列表(列表中没有值时)
print(d1['one'])
d1['one']=1
print(d1['one'])
print(d1['two'])
#——————————————————————————————————————————————————————

dict1={'one':1,'two':2}
dict2={'three':3,'four':4}

chaindict=ChainMap(dict1,dict2)
print(chaindict)
print(chaindict['one'])
print(chaindict['four'])

#——————————————————————————————————————————————————————
dict1={'one':1,'two':2}
readdict1=MappingProxyType(dict1)

print(readdict1)
print(readdict1['one'])
readdict1['three']=3  #返回错误信息,只读字典不可添加或修改,可以修改原字典

OrderedDict可以记住加入键的顺序

defaultdict当访问的键不存在时,可以返回默认类型

ChainMap将多个字典组合,但不是组合成一个字典,当访问键值对时,ChainMap在内部从左至右搜索键,知道找到对应的值为止,若无,则返回错误信息

MappingProxyType创建只读字典

关键要点
  1. 字典时python的核心数据结构
  2. 内置的dict可以满足大多数情况
  3. python的标准库提供了许多要满足特殊需求的字典

数组数据结构
  • 数组的信息是连续的存储在内存块中的,是连续的数据结构
  1. 列表:可变的动态数组

python列表可以包含任意数据类型,包括函数,列表中存储的是PyObject指针,指向不同的对象,而数组是直接存放数据本身

​ 2.元组—不可变容器

元组可包含任意数据类型的元素,添加元素会创建新元组

​ 3.array.array—基本类型数组

此类创建的数组是可变的,行为与列表相似,这种数组是单一数据类型的类型数组

​ 4.str—含有Unicode字符的不可变数组

  • 若需要存储任意对象,且其中含有混合数据类型,可选择列表和元组,前者可变后者不可变
  • 若存储数值数据并要求排列紧密且注重性能,可使用array,还可以找第三方库
集合
  • 集合创建时要注意创建空集合时要使用set()构造函数,若使用{}则会引起起义,会创建一个空字典
set1=set("alice")
set2=set("mkielasd")
print(set1.intersection(set2))
set1.add('one')
print(set1)

  • frozenset—不可变集合

    不可变集合是静态的,只能查询其中的元素

    set1=frozenset({'1','2','a'})
    print(set1)
    print(type(set1))
    
  • collections.Counter—多重集合

背包类型允许在集合中多次出现同一个元素

既要检查元素是否为集合一部分,又要记录元素在集合中出现的次数就可使用该类型

from collections import Counter

obj=Counter()

dict1={'one':1,'two':2}
dict2={'one':3,'four':4}

obj.update(dict1)
print(obj)
obj.update(dict2)
print(obj)
关键要点
  1. 集合是python及其标准库含有的另一种有用且常用的数据结构
  2. frozenset对象可散列且可用作字典和集合的键
  3. collections.Counter实现多重集合或背包类型的数据
栈(后进先出)

栈的插入与删除操作称为入栈(push)和出栈(pop)

  • 列表内置栈数据结构

列表的向前插入元素是一种性能反模式,应尽量避免,若要将列表作为栈,要使用append(),pop()函数,为了获得最佳性能,基于python列表的栈应该向高索引增长并向低索引缩小


  • collections.deque—快速稳健的栈

deque实现了双端队列,支持O(1)实践从两端添加和移除元素

python的deque对象以双链表实现,插入与删除性能非常好,但是访问栈中间元素性能较差,耗时为O(n)

from collections import deque

d=deque()

d.append(1)
d.append(2)
d.append(3)
print(d)

d.pop()
d.pop()
d.pop()
    
关键要点
  1. python中有几个栈的实现,每种实现的性能和使用特性略有不同
  2. collections.deque提供安全快速的通用栈实现
  3. 内置列表类型可以作为栈使用,但要小心只能使用append()和pop()函数来添加删除项,以避免性能下降
队列(FIFO)

队列是含有一组对象的容器,支持快速插入与删除的先进先出语义,插入与删除称为入队(enqueue)和出队(dequeue)

队列通常不允许随机访问所包含的对象

  • collections.deque—快速稳健的队列

deque是支持双端队列的,即可从前后删除与插入

from collections import deque

d=deque()

d.append(1)
d.append(2)
d.append(3)

print(d)

d.popleft()
d.popleft()
d.popleft()

关键要点
  1. python核心语言及其标准库含有几种队列实现
  2. 列表对象可以用作队列,但性能较差,不建议使用
  3. collections.deque非常优秀的队列实现,可用作栈和队列

循环与迭代

range类型标识不可变数列,内存占用比普通列表少,range对象不是存储数列的每个值,而是充当迭代器实时计算数列的值

  • 可以使用enumerate来生成索引列表值

    items=['1','2','3']
    for index,item in enumerate(items):
        print(index,item)
    
关键要点
  1. 在python中编写C风格循环没有python特色,要尽量避免手动管理循环索引和终止条件
  2. python的for循环实际是for-each循环,可直接在容器或序列中的元素上迭代

解析式
关键要点
  1. 解析式是python的一个关键特性,理解和应用解析式可以让代码更加pythonic
  2. 解析式是简单for循环模式的花哨语法糖
  3. 除了列表解析式外,还有字典以及集合解析式

迭代器的实现原理
import time

class Repeater():
    def __init__(self,value):
        self.value=value
        
    def __iter__(self):
        return RepeaterIterator(self)
    
class RepeaterIterator():
    def __init__(self,source):
        self.source=source
        
    def __next__(self):
        return self.source.value
    
rep=Repeater("Hello")
for i in rep:   #实现iter=__iter__(rep)
    print(i)	#实现__next__(iter)方法
    time.sleep(5)
    

#————————————————————————————————————————迭代器的具体实现

repeater=Repeater("hello")
iterator=__iter__(repeater)  #创建iterator作为ReperterIterator实例对象,将repeater作为RepeaterIterator的第二个source参数传进去
__next__(iterator)
__next__(iterator) #self就是iterator,作为参数传进RepeaterIterator的__next__方法中,return返回iterator.repeater.value的值

#---------------------------------------------------------------------------------迭代器的具体实现


一个类中的迭代器
import time

class Repeater():
    def __init__(self,value):
        self.value=value
        
    def __iter__(self):
        return self   #返回可迭代对象,此处的对象即实例对象
    
    def __next__(self):
        return self.value  #返回可迭代对象的值
    
rep=Repeater("one")
for i in rep:
    print(i)
    time.sleep(3)
    
返回有限次的迭代
#迭代器通过异常机制控制代码迭代的运行,看下面的代码:
lst=[1,2,3]
iters=iter(lst)
next(iters)
next(iters)
next(iters)
next(iters)

'''显示结果如下:
StopIteration                             Traceback (most recent call last)
<ipython-input-1-3752b7165c9a> in <module>()
      4 next(iters)
      5 next(iters)
----> 6 next(iters)

StopIteration:'''

引发了StopIteration异常,表示耗尽了迭代其中所有的值

class BoundedRepeater():
    def __init__(self,value,max_repeats):
        self.value=value
        self.max_repeats=max_repeats
        self.count=0
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.count >= self.max_repeats:
            raise StopIteration
        self.count+=1
        return self.value
    
brep=BoundedRepeater("one",4)
for i in brep:
    print(i)
    
#————————————————————————————————————————迭代器具体实现代码

repeater=BoundedRepeater("one",4)
iterator=__iter__(repeater)
while True:
    try:
        item=next(iterator)
    except StopIteration:
        break
    print(item)
关键要点
  1. 迭代器为python对象提供了一个序列接口,占用内存较少且具有python特色,来支持for-in循环之美
  2. 为了支持迭代,都下个需要提供iter和next方法(双下划线)来实现迭代协议
  3. 基于类的迭代器只是用python编写的可迭代对象的一种方法,可迭代对象还包括生成器和生成器表达式

生成器是简化版的迭代器
  • 无限生成器

    生成器使用yield将数据传回给调用者

    def repeater(value):
        while True:
            yield value
    
    rep=repeater("i")
    next(rep)
    print(rep)
    
  • 能够停下来的生成器

    def boundedrepeater(value,max_repeats):
        count=0
        while True:
            if count >= max_repeats:
                return
            count+=1
            yield value
    boundedrepeater("hi",4)
    for i in boundedrepeater("hi",4):
        print(i)
    
    
  • 有限次迭代器优化代码

    def boundedrepeater(value,max_repeats):
        for i in range(max_repeats):
            yield value
            
    boundedrepeater("hi",4)
    for i in boundedrepeater("hi",4):
        print(i)
    
    
关键要点
  1. 生成器函数是一种语法糖,用于编写支持迭代器协议的对象,与编写基于类的迭代其相比,生成器抽象出许多样板代码
  2. yield语句用来暂时终止生成器函数的执行并返回值
  3. 在生成器中,控制流通过yield语句离开会抛出StopIteration异常

生成器表达式

生成器表达式能够更方便的编写迭代器,生成器表达式用一行代码定义迭代器****

  • 生成器表达式一经使用不可重用
iterator=("helo" for i in range(3))
for a in iterator:
    print(a)
    

  • 生成器函数即时生成值

生成器基本模式:

genexpr=(expression for item in collection)

扩展生成器模式:

genexpr=(expression for item in collecton if condition)

等效的代码如下:

def gen():
    for item in colleciton:
        if condition:
            yield item

关键要点
  1. 生成器表达式与列表解析式类似,但不够再列表对象,而是基于类的迭代器,或生成器函数即时生成值
  2. 生成器表达式一经使用不可重用
  3. 生成器表达式最适合简单的实时迭代器,对于复杂的迭代器,最好编写生成器函数或类的迭代器

迭代器链
def printnum():
    for i in range(10):
        yield i
        
def printnum_(seq):
    for i in seq:
        yield i*i

def _printnum(seq):
    for i in seq:
        yield -i
        
list(_printnum(printnum_(printnum())))

out=(x for x in range (10))
out_=(i*i for i in out )
_out=(-i for i in out_)
list(_out)

关键要点
  1. 生成器可以链接在一起形成高效且可维护的数据处理管道
  2. 互相链接的生成器会逐个处理链中的每个元素
  3. 生成器表达式可以用来编写简洁的管道定义,但会降低代码可读性
字典技巧
userid_name={
    320:'bob',
    230:'mike',
    100:'nancy',
}

def greeting(user_id):
    print(userid_name.get(user_id,"there"))

get函数作用:当字典中无所访问的键时,使用默认值

关键要点
  1. 调试包含关系时应避免显示检查字典的键
  2. 建议使用异常处理机制或get方法
排序字典
userid_name={
    320:'bob',
    -230:'mike',
    100:'nancy',
}

sorted(userid_name.items())
sorted(userid_name.items(),key=lambda x: x[1])
sorted(userid_name.items(),key=lambda x: abs(x[0]))
关键要点
  1. 在创建字典和其他集合的有序视图时,可以通过key函数,决定排序方式
  2. key函数时python的一个重要概念
  3. 函数时python的一等公民,在python中无处不在的强大

合并字典
userid_name={
   'a':1,
    'b':2,
    'c':3,
}

dict2={
    'd':4,
    'e':5,
    'f':6,
}
dict4={
    'g':10,
    'j':12,
    'a':100
}

dict3={**userid_name,**dict2,**dict4}
print(dict3)

好啦以上就是python特性的笔记啦,希望对小白们有所帮助

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

疯吶psycho

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值