不定长参数
位置不定长参数,获取参数args会整合为一个元组
def info(*args):
print('arg is', args)
print('type(arg) is', type(args))
info(1, 2, 3, 4, 'a', 'b')
# 输出
# arg is (1, 2, 3, 4, 'a', 'b')
# type(arg) is <class 'tuple'>
关键字不定长参数,,获取参数kwargs会整合为一个字典
def info2(**kwargs):
print('kwargs is', kwargs)
print('type(kwargs) is', type(kwargs))
for k, v in kwargs.items():
print(k, v)
print(kwargs['age'])
# 测试数据
info2(name='Bob', age=20, sex='1')
# 输出
# kwargs is {'name': 'Bob', 'age': 20, 'sex': '1'}
# type(kwargs) is <class 'dict'>
# name Bob
# age 20
# sex 1
# 20
函数作为参数
def func(compute):
print(type(compute)) # <class 'function'>
result = compute(1, 2)
return result
def compute(x, y):
return x + y
print(func(compute(1,2))) # == func(3)
# 错误发生是因为您直接将compute(1, 2)的结果作为参数传递给func()函数,
# 而不是传递函数本身。当您调用compute(1, 2)时,它返回一个整数(x和y的和)
print(func(compute))
匿名函数
def定义的函数有名字,可以重复多次使用
但是lambda定义的函数只能使用一次
无法写多行
意会...
def func(compute):
print(type(compute)) # <class 'function'>
result = compute(1, 2)
return result
# def compute(x, y):
# return x + y
print(func(lambda x, y: x + y)) # 3
print(func(lambda *args: sum(args))) # 3
异常
try:
a = 1 / 0
print(a)
except: # 捕获所有异常
print('出错了1')
else: # 没有出错执行这里
print('没有出错')
finally:
print('不管有没有出错,都会执行这里')
try:
a = 1 / 0
print(a)
except NameError as e:
print(e)
print('出错了2')
except ZeroDivisionError as e:
print(e)
print('除数不能为0')
except Exception as e: # 上面捕获到了,这里不再执行
print(e)
print('出错了e')
Python包
从物理上看,包就是一个文件夹,在该文件夹下包含了一个_init_.py文件,该文件夹可用于包含多个模块文件从逻辑上看,包的本质依然是模块
testpackage.__init__.py
print('testpackage package __init__')
testpackage.run.py
print('run')
def fun1():
print('fun1')
testpackage.py执行查看顺序
import testpackage.run
testpackage.run.fun1()
# testpackage package __init__
# 这是run.py的print输出
# 这里是run.py的fun1函数
对象
构造
class Student:
name = '默认'
age = 999
# self表示类对象本身,只有通过self才能访问到对象属性
def __init__(self, namex, agex):
print(f"初始化:{self.name}-{self.age} -> {namex}-{agex}")
self.name = namex
self.age = agex
stu1 = Student('小明', 18)
# 初始化:默认-999 -> 小明-18
内置方法
👾
__new__()
:创建并返回一个实例,这是在__init__()
之前调用的。
__init__()
:初始化新创建的对象。
__del__()
:当对象即将被垃圾回收时调用,常用于资源清理。
__getattr__()
、__setattr__()
、__delattr__()
:控制属性的获取、设置和删除。
__call__()
:使对象可调用,像函数一样使用。
__str__(self)
__str__()
是 Python 中的一个特殊方法或者说魔术方法,主要用于定义类实例作为字符串表示的形式。当你试图打印一个对象或者将一个对象转换为字符串时,Python 会调用这个方法。
print(str(stu1)) # == print(stu1)
改造了__str__, 小明-18
__lt__(self, other)
__lt__()
是 Python 中的一个特殊方法,也称为魔术方法,用于比较两个对象的大小关系,具体来说是实现小于 (<
) 操作符的功能。当一个对象实例 a 调用 a < b
时,如果 a 类型定义 __lt__()
方法,则该方法会被自动调用。
def __lt__(self, other):
return True if self.age < other.age else False
print(stu1 < stu2) # True
print(stu1 > stu2) # False
当执行 num1 < num2
时,Python 会自动调用 num1.__lt__(num2)
方法,根据其中的逻辑判断。如果需要与其他类型进行比较,一般还需要考虑类型检查以避免错误。
__eq__(self, other)
判断等于
为什么类定义函数要传入self
在Python中,子类定义函数(实际上是方法,即绑定到类实例的方法)时,需要传入self
参数作为第一个参数,这是因为:
- 标识实例方法:
self
参数标志着这是一个类的方法,而不是一个独立的函数。在面向对象编程(OOP)中,类的方法通常需要操作或访问类实例的状态(即实例变量)。self
代表调用该方法的类实例本身,它让方法有了上下文,能够在方法内部访问和修改实例的属性。 - 隐式传递实例:当你通过类的实例调用一个方法时,Python会自动将实例作为第一个参数传给方法,这个参数在方法内部被称作
self
。例如,当你创建一个类Person
并调用其say_hello
方法时,实际上是这样运作的:
class Person:
def say_hello(self):
print(f"Hello, my name is {self.name}.")
person = Person(name="Alice")
person.say_hello() # 在内部,Python自动做了 person.say_hello(person)
- 一致性与可读性:在Python中,这是一种约定俗成的标准,有助于提高代码的可读性和一致性。所有类的方法都应该接受
self
作为第一个参数,以便开发者一看就知道这是一个类方法,并且能够方便地访问和操作实例的属性和方法。
总结起来,self
参数的存在是为了让方法能够与类实例绑定在一起,并在方法内部访问和修改实例状态。虽然在技术层面上可以使用其他名称替代self
,但由于这是Python编程社区广泛接受的约定,因此强烈建议始终使用self
作为类方法的第一个参数。
封装
内部变量不让外面用
在Python中,尽管不像某些其他编程语言那样具有明确的private
关键字,Python提供了约定俗成的方式来标记和实现私有成员变量(以及方法)。要声明一个私有变量,通常在变量名前加两个下划线(__
)作为前缀,例如:
class Phone:
__current_vlotage = None
def __keep_single_core(self):
print('单核')
xiaomi = Phone()
# xiaomi.__keep_single_core()
# AttributeError: 'Phone' object has no attribute '__keep_single_core'. Did you mean: '_Phone__keep_single_core'?
xiaomi._Phone__keep_single_core() # 可以这么取,但不建议
# print(xiaomi.__current_vlotage)
print(xiaomi._Phone__current_vlotage)
这主要是为了防止子类无意间覆盖基类的内部实现细节,而不是为了完全禁止访问。
继承
方式1:
调用父类成员
使用成员变量:父类名.成员变量
使用成员方法:父类名.成员方法(self)
方式2:
使用super()调用父类成员
使用成员变量:super().成员变量
使用成员方法:super().成员方法()
class Phone:
IMEI = '123456789'
producer = 'Apple'
def call_by_4g(self):
print('4G')
class Camera:
cmos = 'IMX989'
producer = 'SONY'
def take_photo(self):
print('take photo')
# 多继承子类
class iPhone5s(Phone, Camera):
def call_by_4g(self):
super().call_by_4g()
def call_by_5g(self):
print('5G')
def take_photo(self):
super().take_photo()
p = iPhone5s()
p.take_photo()
print(p.producer) # 同名属性,先继承Phone的producer
多态
函数的形参是父类,其实可以传入子类
类型注解
🐮🐴,就是为了提示IDE这是什么类型数据,使其可以提示,就是一个标记,写错了不影响程序
ll: list[int] = [1,2,3,4,5,6]
def fun(x:int, y:int)->int:
return x+y
# Union注解
from typing import Union
mylist: list[Union[str,int]] = [1,2,6,'cxk',7,'asshole',0]
mydict: dict[str,Union[str,int]]={"name":"cxk", "age":20}
操作数据库
MySQL :: Download MySQL Installer
基本框架
from pymysql import Connection
conn = Connection(host='localhost',
port=3306,
user='root',
password='123456',
autocommit=True
)
print(conn.get_server_info()) # 获取服务器信息8.0.36
cursor = conn.cursor()
conn.select_db('mysql')
# cursor.execute('create table test_pymysql(id int);')
cursor.execute('select * from user')
# 取得查询结果,是一个元组
results: tuple = cursor.fetchall()
for result in results:
print(result)
conn.close()
闭包
如何避免某个变量被访问和随便修改
基础闭包,本质上就是返回内部的一个函数
def outer(logo):
def inner(msg):
print(f'<{logo}>{msg}</{logo}>')
return inner
fn1 = outer('Apple')
fn1('se3')
fn1('15 Pro')
# <Apple>se3</Apple>
# <Apple>15 Pro</Apple>
fn2 = outer('Google')
fn2('Pixel X')
fn2('Pixel 5')
# <Google>Pixel X</Google>
# <Google>Pixel 5</Google>
def outer(num1):
def inner(num2):
# num1+=num2 # num1无法修改
# cannot access local variable 'num1'
# where it is not associated with a value
nonlocal num1 # 允许修改
num1 += num2
print(num1)
return inner
fn = outer(10)
fn(5)
优点:
无需定义全局变量即可实现通过函数,持续的访问、修改某个值
闭包使用的变量的所用于在函数内,难以被错误的调用修改
缺点:
由于内部函数持续引用外部函数的值,所以会导致这一部分内存空间不被释放,一直占用内存
装饰器
装饰器其实也是一种闭包,其功能就是在不破坏目标函数原有的代码和功能的前提下,为目标函数增加新功能。
def outer(func):
def inner(*args):
print('sleep start')
func(args[0])
print('sleep end')
return inner
fn = outer(sleep)
fn(3)
设计模式
单例模式
创建类的实例后,就可以得到一个完整的、独立的类对象。
它们的内存地址是不相同的,即t1和t2是完全独立的两个对象。
某些场景下,我们需要一个类无论获取多少次类对象,都仅仅提供一个具体的实例用以节省创建类对象的开销和内存开销(比如微信只能开一个?)
🐎的,没下文了?就这么实现的?java都没这样啊
工厂模式
class Person:
pass
class Student(Person):
pass
class Teacher(Person):
pass
class Factory:
def create_person(self, type):
if type == 'student':
return Student()
elif type == 'teacher':
return Teacher()
else:
return None
factory = Factory()
student = factory.create_person('student')
teacher = factory.create_person('teacher')
print(student)
print(teacher)
使用工厂类的get_person()方法去创建具体的类对象
优点:
大批量创建对象的时候有统一的入口,易于代码维护,当发生修改,仅修改工厂类的创建方法即可
符合现实世界的模式,即由工厂来制作产品(对象)(优雅)
没有线程万万不能
import threading
from time import sleep
# threading_obj = threading.Thread(group=None,
# target=None,
# args=None, 元组传参
# kwargs=None,字典传参
# name=None,
# daemon=None)
def fun1():
while True:
print('|||||||||||||||||')
sleep(1)
def fun2():
while True:
print('—————————————————')
sleep(1)
def sing1(huabei):
while True:
print(huabei)
sleep(1)
def sing2(sihai):
while True:
print(sihai)
sleep(1)
if __name__ == '__main__':
# th1 = threading.Thread(target=fun1)
# th2 = threading.Thread(target=fun2)
# th1.start()
# th2.start()
huabei = threading.Thread(target=sing1, args=('华北无浪漫',))
sihai = threading.Thread(target=sing2, kwargs={'sihai': '死海扬起帆'}) # 🐮🐴这里的key设计一定要和形参一致?
huabei.start()
sihai.start()
网络编程
服务端
import socket
# 创建socket对象
socket_server = socket.socket()
# 绑定ip地址端口
socket_server.bind(('localhost', 8888))
# 监听端口
socket_server.listen(1) # 接受链接数量
# 等待客户端链接
# result: tuple = socket_server.accept()
# conn = result[0] # 客户端服务端链接对象
# address = result[1] # 客户端地址信息
conn, address = socket_server.accept() # 阻塞方法
# 接受客户端消息
print('client', address, ': ')
while True:
print(conn.recv(1024).decode('utf-8'))
# 回复
msg = input('回复:')
if msg == 'exit':
break
conn.send(msg.encode('utf-8'))
# 关闭链接
conn.close()
socket_server.close()
客户端
import socket
# 创建socket对象
socket_client = socket.socket()
socket_client.connect(('localhost', 8888))
while True:
msg = input('发送:')
if msg == 'exit':
break
socket_client.send(msg.encode('utf-8'))
rec = socket_client.recv(1024)
print('收到回复', rec.decode('utf-8'))
socket_client.close()