变量
- python的内存变量不需要类型
- Python万物皆对象,在栈内存永远存储的是数据的堆内存地址,堆内存存储的数据值。
变量的注解
在 Python 中定义变量是不需要指定类型的。但是在 Python 中, 变量注解(Variable Annotations)是 Python 3.5 引入的一个新特性, 它允许开发者为变量和函数参数提供预期的类型信息。
name: str = "逗比" # 定义姓名
age: int = 30 # 定义年龄
height: float = 1.78 # 定义升高
is_has_jj: bool = True # 定义是否有jj
is 关键字
a = 10
b = 20
print(a is b) # False,a和b指向的是不同的地址
a = 10
b = a
print(a is b) # True,a和b指向的是相同的地址
小整数对象池
这是因为编译器Cpython中存在小整数池[-5, 256]
,在解释器启动的时候就自动开辟了, 在全局范围内重复使用,不会被垃圾回收。
有时,同时创建2个相同的超过了-5,256的数据,也可能会相等,因为python自身做了优化,间隔很小的时间,创建2个相同的元素,他们的地址相同。
id 函数
使用 id 函数获取变量的内存地址:
a = 10
b = 10
print(a is b)
print(id(a))
print(id(b))
---output---
True
4299450960
4299450960
del 关键字
del的作用是删除变量,解除变量和堆中对象的关联。
type 函数
print(type(10))
print(type(3.14))
print(type("100"))
字符串
text1 = "我是一个字符串"
text2 = '我是一个字符串'
text3 = """我是一个字符串,
我还可以换行"""
str = "Hello \'Python\'"
str = "Hello \\t \"Python\""
str = "Hello \t \"Python\""
---output---
我是一个字符串
我是一个字符串
我是一个字符串,
我还可以换行
Hello 'Python'
Hello \t "Python"
Hello "Python"
占位符
name = "张三"
age = 18
money = 3.14
print("Hello, 我是%s, 年龄%-4d, 我有%.4f块钱" % (name, age, money))
---output---
Hello, 我是张三, 年龄18 , 我有3.1400块钱
快速格式化
通过在字符串前面添加一个f,可以在字符串中使用 {} 嵌入变量。
name = "张三"
age = 18
money = 3.14
print(f"Hello, 我是{name}, 年龄{age}, 我有{money}块钱")
---output---
Hello, 我是张三, 年龄18, 我有3.14块钱
流程控制
range
for i in range(5):
print(i)
print("----")
for i in range(5, 10):
print(i)
print("----")
for i in range(5,10,2):
print(i)
0
1
2
3
4
----
5
6
7
8
9
----
5
7
9
容器
list
name_list = ["zhangsan", "lisi", 1, "zhaoliu", 3, "chenba"] # 定义一个容器
print(name_list[-1])
print(name_list[-3])
print(name_list[2])
print(len(name_list))
print(name_list.index(1, 0, 4))
# add
name_list.append(1)
# insert
name_list.insert(1, "OK")
print(name_list)
# del
name_list.pop(4)
print(name_list)
del name_list[4]
print(name_list)
# count
cnt = name_list.count(1)
print(cnt)
# 列表推导式
int_list =[1,2,3,5,4]
new_list = [i*2 for i in int_list]
print(new_list)
# join
str_list =["1","2","3","5","4"]
print("_".join(str_list))
---output---
chenba
zhaoliu
1
6
2
['zhangsan', 'OK', 'lisi', 1, 'zhaoliu', 3, 'chenba', 1]
['zhangsan', 'OK', 'lisi', 1, 3, 'chenba', 1]
['zhangsan', 'OK', 'lisi', 1, 'chenba', 1]
2
[2, 4, 6, 10, 8]
1_2_3_5_4
切片
序列[起始下标:结束小标:步长]
num_list = [1, 2, 3, 4, 5]
new_list2 = num_list[1:4]
new_list = num_list[:4:2]
new_list = num_list[:]
new_list = num_list[3:1:-1]
tuple
因为元组中的元素是不能修改的,修改会报错。
set
name_set = {"zhangsan", "lisi", 1, "zhaoliu", 3, "chenba"} # 定义一个容器
print(name_set)
name_set.add("add")
name_set.pop()
name_set.remove("add")
name_set.union(name_set)
name_set.difference(name_set)
name_set.difference_update(name_set)
dict
dict_a = {"d":1,"a":3}
dict_a.get("d")
function
def triple_print(a: int, b: None, c=3):
"""
默认值
b:None
c=3
list = [1, 2, 3]
triple_print(*list)这样调用是序列实参
dict01 = {"a": 1, "c": 3, "b": 2}
triple_print(**dict01)这样调用是字典实参
triple_print(b=3,a=4,c=5)这样是通过参数名称来传递值
"""
print(f"{a},{b},{c}")
def four_print(a=2, *b, d, **c) -> tuple:
"""
-> tuple标明了函数的返回值类型
*b:是一个元组,接收任意个数的参数
**c:通过字典可以接收到数据
# args 是一个元组,接收任意个数的参数
可以多返回值
"""
print(f"{a},{b},{c},{d}")
return a, b
变更的作用域
- 局部变量:函数内部定义的变量,函数执行完毕后,变量会自动销毁
- 全局变量:函数外部定义的变量,函数执行完毕后,变量仍然存在
- globa 关键字,获取全局变量
num = 10
def fun_a():
num = 20
print(f"函数内:{num}")
fun_a()
print(f"函数外:{num}")
输出的是函数内:20 函数外:10
通过 global 关键字,告诉程序,我这个是全局变量。
输出的是函数内:20 函数外:20
Union类型
from typing import Union
my_list: list[Union[int, str]] = [1, "name"] # 定义了list中的元素是多种数据类型
my_dict: dict[str, Union[int, str]] = {"name": "zhangsan", "age": 18} # 定义了value可以是多种类型
def fun(data: Union[int, str]) -> Union[int, str]: # 定义返回值的类型注解
pass
def fun(data: Union[int, str]) -> list[Union[int, str]]: # 定义list返回值的类型注解
pass
类
class Student:
stu_count = 0; 这是一个类变量
def __init__(self, sid, name, age):
self.sid = sid # 学号
self.name = name # 姓名
self.age = age # 年龄
Student.stu_count += 1
@classmethod
这个类方法没有self变量,所以不能通过实例来访问,只能通过类名来访问
def get_stu_count(cls):
print(f"一共创建了{Student.stu_count}个学生")
stu1 = Student("001", "张三", 18)
stu2 = Student("002", "李四", 19)
stu2 = Student("003", "王五", 17)
Student.get_stu_count()
static method
静态方法的作用是用来定义一些工具类函数。
在静态方法中不能访问实例成员和类成员。
class Math_Util:
@staticmethod
def get_max(a, b): # 获取大的数
return a if a > b else b
@staticmethod
def get_min(a, b): # 获取小的数
return a if a < b else b
max_num = Math_Util.get_max(11, 15)
print(max_num)
min_num = Math_Util.get_min(11, 15)
print(min_num)
inner method
def __str__(self):
return f"sid:{self.sid}, name:{self.name}, age:{self.age}"
def __lt__(self, other):# other表示另一个对象,返回值为True或Flase
return self.age < other.age
def __le__(self, other):# other表示另一个对象,返回值为True或Flase
return self.age <= other.age
def __eq__(self, other):# other表示另一个对象,返回值为True或Flase
return self.age == other.age
def __repr__(self):
return f"Student('{self.sid}', '{self.name}', {self.age})"
stu1 = Student("001", "张三", 18)
stu_str = repr(stu1) # 获取__repr__()方法返回的string数据
print(stu_str)
stu2 = eval(stu_str) # 将string执行,返回的是一个对象,相当于克隆了一个对象
print(stu2.name)
print(stu1 == stu2)
私有属性和方法
定义私有成员的方式:
定义私有成员变量:变量名以 __开头,2个下划线开头 定义私有成员方法:方法名以 __开头,2个下划线开头
class Phone:
def __init__(self):
self.producer = "华为" # 手机品牌
self.__voltage = 12 # 电压
def call(self):
print("打电话")
print(f"手机品牌:{self.producer}")
print(f"手机电压:{self.__voltage}")
# 定义一个私有方法
def __get_run_voltage(self):
print(f"当前电压:{self.__voltage}")
phone = Phone()
phone._Phone__voltage = 24 # 修改私有属性
phone.call()
phone._Phone__get_run_voltage() # 调用私有方法
私有属性和私有方法是骗人的障眼法,完全可以通过 _类型__私有属性 和 _类型__私有方法 的形式来调用。
getter和setter的初级进化
producer = property(get_producer, set_producer)
voltage = property(get_voltage, set_voltage)
这是给属性绑定了getter和setter方法,这样,在访问属性的时候,会直接调用getter和setter方法。
所以 self.producer 和 phone.producer 都是调用的是方法,而不是属性,通过执行结果可以看出来,或者通过断点调试。
getter和setter的终极写法
class Phone:
def __init__(self, producer, voltage):
self.producer = producer # 访问的不是属性,是方法
self.voltage = voltage # 访问的不是属性,是方法
@property
def producer(self):
return self.__producer
@producer.setter
def producer(self, producer):
self.__producer = producer
@property
def voltage(self):
return self.__voltage
@voltage.setter
def voltage(self, voltage):
if voltage < 36:
self.__voltage = voltage
else:
raise ValueError("参数错误") # 限制传入的参数值,进行报错处理
phone = Phone("华为", 12)
print(phone.producer) # 调用的是方法,不是属性
print(phone.voltage)
phone.producer = "小米" # 调用的是方法,不是属性
phone.voltage = 24
print(phone.producer)
print(phone.voltage)
每个属性对应两个与属性名相同的方法,分别用于获取和设置属性,并在方法上添加 @property 和 @属性.setter 注解。
__slots__
我们之前可以通过 对象.属性 = 值 随时为一个对象添加属性。
__slots__
的作用就是限制一个类创建的实例只能有固定的属性,不能随意添加属性。
class Phone:
__slots__ = ("producer", "__voltage")
def __init__(self, producer, voltage):
self.producer = producer
self.__voltage = voltage
phone = Phone("华为", 12)
phone.size = 6 # 报错:AttributeError: 'Phone' object has no attribute 'size'
继承判定函数
- type() 函数:type()函数可以判断某个对象是否是某个类型,注意:子类的对象不是父类的对象的类型。
- isinstance()函数:isinstance()函数可以判断某个对象是否是某个类型的实例,子类对象也是父类对象的实例。
- issubclass()函数:issubclass()函数可以用来判断一个类是否是另外一个类的子类。
多继承的解析顺序
如果继承的多个父类拥有同样的属性和方法,那么会使用哪个父类的呢?
会使用先继承的父类的,后继承的无效,如果父类中都没有,则会去父类的父类寻找,一直向上找。
即使类没有写明继承其他类,但是所有的类都直接或间接继承自Object类。
抽象类
含有抽象方法的类成为抽象类。抽象方法就是没有方法体,方法体为空的方法。
class Vehicle:
def transport(self, destination):
pass
JSON
import json
class Student:
def __init__(self, sid, name, age):
self.sid = sid
self.name = name
self.age = age
if __name__ == '__main__':
# 将字典转换为JSON数据
stu_dict = {"sid": "001", "name": "zhangsan", "age": 18}
json_data = json.dumps(stu_dict, ensure_ascii=False) # 使用dumps()方法,将字典转换为JSON数据
print(json_data)
print(type(json_data))
# 将JSON数据转换为字典
json_data = '{"sid": "001", "name": "张三", "age": 18}'
stu_dict = json.loads(json_data) # 使用loads()方法,将JSON数据转换为字典
print(stu_dict["name"])
# 对象转换为JSON
stu = Student("001", "张三", 98)
stu_json = json.dumps(stu.__dict__, ensure_ascii=False) # 获取对象的字典格式
print(f"对象转换为json:{stu_json}")
# JSON转换为对象
json_str = '{"sid": "001", "name": "张三", "age": 98}'
stu_dict = json.loads(json_str) # 首先将JSON字符串转换为字典
print(stu_dict["name"])
stu = Student(**stu_dict) # 然后通过双星号字典形参传递给构造方法,创建对象,这个类,需要有构造方法
print(stu.name)
模块和包
每一个以 .py 结尾的python文件就是一个模块。 包是一个一个文件夹,下面有许多的模块
import 模块名 # 导入单个模块
import 模块名1, 模块名2 # 导入多个模块
import time # 导入时间模块,就有一个time的py文件
from 模块名 import 功能名
from 模块名 import 功能名 as 别名 # 功能也可以起别名
from time import sleep # 只导入模块中的sleep方法
from 模块名 import *
from time import * # 只导入模块中所有的函数
模块的搜索顺序
python解释器在导入模块的时候,会搜索当前目录指定模块名的文件,如果有就直接导入,如果没有在搜索系统目录。
所以在开发的时候,给文件起名,不要和系统的模块文件重名,否则会导致调用的系统功能无效。