变量
- 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解释器在导入模块的时候,会搜索当前目录指定模块名的文件,如果有就直接导入,如果没有在搜索系统目录。
所以在开发的时候,给文件起名,不要和系统的模块文件重名,否则会导致调用的系统功能无效。