项目中python一些方法的用法
1.文件路径获取
from pathlib import Path
project_root = Path(__file__).resolve().parent.parent
| 表达式 | 说明 |
|---|---|
__file__ |
当前文件的路径(可能是相对路径或绝对路径,取决于执行方式) |
Path(__file__) |
用 pathlib.Path 对象包装该路径,便于后续使用路径操作方法 |
.resolve() |
将路径解析为绝对路径,同时消除符号链接、.. 和 . 等,得到真实路径 |
.parent |
获取当前路径的上一级目录(即父目录) |
.parent.parent |
连续获取两次父目录,也就是上两级目录(祖父目录) |
2.约定私有化
self._config = self._load_config() # 加载配置并保存
_load_config 前面的单下划线是约定,表示“这个方法是类内部使用的,外部代码不应直接调用”。
它不是真正的访问控制,外部仍然可以调用 obj._load_config(),但 IDE 会给出提示,团队也会遵循这个约定。
3.yaml文件内容读成字典类型
if not self.config_path.exists():
raise FileNotFoundError(f"配置文件不存在: {self.config_path}")
self.config_path 应当是一个 pathlib.Path 对象(通常由 Path(config_file) 得到)。
exists() 方法判断路径是否存在(文件或目录)。
raise 就是“手动触发错误”的关键字,用于在检测到不合理条件时,主动告知调用者问题所在。
如果不存在,就抛出 FileNotFoundError,并附带清晰的错误信息,告知哪个文件缺失。
好处:尽早失败(fail-fast),避免后续 open 时抛出更模糊的异常。
with open(self.config_path, 'r', encoding='utf-8') as f:
return yaml.safe_load(f)
使用 with 语句打开文件,确保使用完毕后自动关闭。
'r' 表示只读模式,encoding='utf-8' 指定字符编码,避免中文乱码。
yaml.safe_load(f) 将 YAML 文件内容解析为 Python 对象(通常是字典)。safe_load 比 load 更安全,因为它不会执行任意 Python 代码,防止潜在的安全问题。
直接将解析结果 return 给调用方,由调用方(如 __init__ 中的 self._config = self._load_config())保存。
过程:1.判断我们提供的config_path地址是否存在 -> 2.以读的形式把读到的内容保存到f ->
3.返回使用yaml软件包中的方法,将读到的内容f,按照字典的格式解析的对象
4.装饰器@property
@property 是 Python 中一个非常实用的内置装饰器,它将类中的方法“伪装”成属性,让你可以像访问属性一样调用方法,同时还能在背后执行逻辑(如校验、计算、懒加载)。
class Person:
def __init__(self, name):
self.name = name # 实例属性
self.age = 18
def greet(self): # 实例方法
return f"Hello, I'm {self.name}"
def have_birthday(self): # 方法有副作用
self.age += 1
# 使用
p = Person("Alice")
# 访问属性
print(p.name) # 'Alice' → 直接取值
print(p.age) # 18
# 调用方法
print(p.greet()) # 'Hello, I'm Alice' → 必须加括号
p.have_birthday() # 修改状态,没有返回值
print(p.age) # 19
class Square:
def __init__(self, side):
self._side = side
@property
def area(self):
return self._side ** 2
s = Square(5)
print(s.area) # 25 → 注意没有括号,实际调用了 area 方法
5.isinstance和type的差别
type:
1.严格需要精确类型,例如序列化时只处理原生 dict 而不处理子类(避免意外属性被序列化)。
2.性能敏感且确认不需要考虑继承(比如类型比较在热循环中,但这种情况很少)。
3.调试或元编程中,需要准确知道对象的原始类,而不是它的父类。
isinstance:
1.符合面向对象的多态原则:子类应当可以替代父类,所以判断类型时应该承认子类实例也是父类类型。
2.代码更健壮:如果将来把 dict 换成 collections.UserDict 或自定义子类,isinstance 仍能正常工作,而 type 就会出错。
3.支持抽象基类和协议:比如判断“是否可迭代”用 isinstance(obj, Iterable) 比判断具体类型更通用。
class Animal:
pass
class Dog(Animal):
pass
dog = Dog()
print(isinstance(dog, Animal)) # True → Dog 是 Animal 的子类
print(type(dog) is Animal) # False → type(dog) 是 Dog,不是 Animal
print(type(dog) is Dog) # True → 精确匹配
6.初始化数据库连接 pymysql.connect()
def __init__(self):
"""按 config 里 db 那段建个连接"""
cfg = Config()
self.conn = pymysql.connect(
host=cfg.get("db.host", "localhost"),
port=int(cfg.get("db.port", 3306)),
user=cfg.get("db.user"),
password=cfg.get("db.password"),
database=cfg.get("db.database"),
charset=cfg.get("db.charset", "utf8mb4"),
cursorclass=pymysql.cursors.DictCursor,
autocommit=True,
)
调用 pymysql.connect() 建立与 MySQL 数据库的真实连接,并将连接对象保存到实例变量 self.conn 中。
7.查询数据库一条数据
def query_one(self, sql, params=None):
"""执行 sql 拿一行"""
try:
with self.conn.cursor() as cursor:
cursor.execute(sql, params or ())
return cursor.fetchone()
except Exception as e:
log.error(f"query_one execution failed: {e}")
raise
def get_user_by_usernam(self, username):
"""用户名去 yonghu 表查一条,注册断言用"""
sql = "SELECT id, yonghuming FROM yonghu WHERE yonghuming=%s"
return self.query_one(sql, (username,))
conn.cursor() 的作用是从数据库连接对象中创建一个游标(Cursor)对象。
游标是数据库编程中的一个重要概念,相当于一个“指针”或“工作区”,用于:执行 SQL 语句(execute())、获取执行结果(fetchone()、fetchall() 等)、控制数据的逐行处理。
cursor.execute(sql, params or ()) 的作用是执行一条 SQL 语句,并附带参数。
执行 SQL:将 sql 字符串(如 "SELECT * FROM users WHERE id = %s")发送给数据库执行。
参数化查询:第二个参数提供 SQL 中占位符(%s)对应的实际值。使用参数化可以有效防止 SQL 注入,并能正确处理特殊字符。
params or ():如果传入的 params 为 None,则用空元组 () 代替,避免 execute 因收到 None 而报错;如果 params 有值(如 (10,) 或 {"id": 10}),则直接使用。
cursor.fetchone()只拿出查到数据的第一条数据,如果想拿到全部的查到的所有数据就得使用cursor.fetchall().
8.清空数据库并添加默认账户
def reset_yonghu_table(self):
"""truncate 清空表再塞个默认账号"""
try:
with self.conn.cursor() as cursor:
cursor.execute("TRUNCATE TABLE yonghu")
insert_sql = """
INSERT INTO yonghu (yonghuming, mima, xingming, xingbie, youxiang, shouji, money)
VALUES (%s, %s, %s, %s, %s, %s, %s)
"""
cursor.execute(insert_sql, ('pengbei', 'q1216530240', '鹏杯', '男', '1216530240@qq.com', '13800000000', 0))
log.info("yonghu table truncated and default user (username=1, password=1) inserted.")
except Exception as e:
log.error(f"reset_yonghu_table failed: {e}")
raise
只要你看得懂7,那你就很轻松看得懂8,cursor.execute("sql语句")就是执行你给的sql语句。Python 中的多行字符串字面量,使用 三个双引号 """(或三个单引号 ''')作为界定符。它的特点是字符串内容中可以包含换行符,代码中的换行和缩进会被保留,它与用单引号 '...' 或双引号 "..." 定义的字符串在运行时完全一样,只是书写方式不同,常用于长文本或格式化内容
比如 SQL 语句、HTML/JSON 模板、帮助文档等,方便阅读和维护。
9 统一单元格数据类型
class Excel:
@staticmethod
def _clean_cell(value):
"""格子里的数啊空啊统一搓成字符串,省得 123.0 这种坑"""
if pd.isna(value):
return None
if isinstance(value, float) and value.is_integer():
return str(int(value))
return str(value)
print(Excel._clean_cell(12.0)) #输出是12
这段代码是一个静态方法,用于“清洗”单元格数据,主要目标是将各种输入统一转换为字符串或 None,并处理浮点数中整数值的特殊显示问题(避免 123.0 这种形式)。
@staticmethod:声明为静态方法,无需类实例或类参数,可通过 ClassName._clean_cell(x) 直接调用。
| @staticmethod优点 | 说明 |
| 逻辑内聚 | 工具函数归入类,一目了然 |
| 避免命名冲突 | 用类名作命名空间,减少全局函数 |
| 无需实例化 | 类名直接调用,省去创建对象的成本 |
| 自文档化 | 清楚表明不依赖实例/类状态,是纯函数 |
| 内部可重用 | 实例方法中也可以调用,作为辅助工具 |
| 继承友好(有限) | 子类可重写,但无 cls 自动识别 |
| 微小性能提升 | 少了参数传递绑定开销 |
使用 pandas.isna() 检测 value 是否为缺失值(包括 None、NaN、NaT 等)。若为缺失值,返回 None。
value.is_integer() 是 浮点数(float)对象的一个方法,用于判断该浮点数的小数部分是否为 0(即它是否代表一个整数)。
10 读取Excel表格
@staticmethod
def _read_and_clean(file_path, sheet_name=0):
"""读表再整表 map 一遍清洗"""
df = pd.read_excel(file_path, sheet_name=sheet_name, dtype=object)
return df.map(Excel._clean_cell)
使用 pandas.read_excel() 读取指定工作表。
dtype=object:强制将所有列的数据类型设为 object(即 Python 对象)。
这样做可以避免 pandas 自动推断类型(例如数字变成 int64 或 float64,日期变成 datetime),从而让后续的单元格清洗统一处理字符串、数字、空值等。
file_path:Excel 文件路径。
sheet_name=0:工作表名称或索引,默认 0(第一个工作表)。
df.map(func):将 func 应用到 DataFrame 的每个单元格(逐元素操作)。
Excel._clean_cell 是之前定义的静态清洗函数(处理 pd.isna、浮点数整数化、转字符串等)。
最终返回一个新的 DataFrame,其中每个单元格都经过了清洗。
11 用元组打包列信息
@staticmethod
def get_login_data_from_excel(file_path, sheet_name=0):
"""登录三列拉出来,zip 成一组组给 parametrize"""
df = Excel._read_and_clean(file_path, sheet_name)
data = list(zip(df['username'], df['password'], df['result']))
sheet_name=0时取Exsel的第一行数据。
zip() 的作用是将多个可迭代对象(如列表)中对应位置的元素打包成元组,返回一个迭代器。
df['username']、df['password']、df['result'] 是三个列,每个列都有相同数量的行。
zip(...) 会将每一行的三个值取出来,组成一个元组 (username, password, result)。
list(...) 将所有这样的元组收集成一个列表,最终 data 的结构类似:[('user1', 'pass1', '成功'), ('user2', 'pass2', '失败'), ...]
12 Base中使用的一些方法
12.1 创建驱动对象
def __init__(self, driver=None):
"""没传 driver 就新开一个 Chrome"""
if driver:
self.driver = driver
else:
service = Service(
executable_path="D:\\python\\chromedriver.exe")
self.driver = webdriver.Chrome(service=service)
self.driver.implicitly_wait(10)
self.driver.maximize_window()
这段代码它的核心作用是初始化一个浏览器驱动对象(WebDriver),供类中其他方法进行网页元素定位、操作、断言等自动化工作。
raise e 是 Python 中重新抛出已捕获的异常的写法。它出现在 except 块内部,用于将之前捕获到的异常对象 e 再次向上层(调用方)抛出。
12.2处理弹窗
def alert_text(self):
"""原生 alert"""
try:
alert = self.driver.switch_to.alert
text = alert.text
log.info(text)
alert.accept()
return text
except Exception as e:
raise e
这段代码的作用是处理浏览器中原生的 JavaScript Alert(警告框)弹窗:获取弹窗中的文本内容,记录日志,然后点击“确定”按钮(accept),最后将文本内容返回。
alert.accept():Alert 对象的方法,点击弹窗上的确定按钮。
13 标签参数化
@pytest.mark.parametrize("username, password, result", login_test_cases)
来源:pytest 框架
作用:实现参数化测试,让同一个测试函数使用多组不同的参数运行多次。
"username, password, result":参数名列表。
login_test_cases:可迭代对象(通常是一个列表,每个元素是一个包含三个值的元组),依次为每个参数提供具体值。
效果:pytest 会为 login_test_cases 中的每一组数据生成一个独立的测试用例。例如,如果 login_test_cases 有 5 组数据,就会生成 5 个测试,分别检查用户名、密码的输入组合与预期结果(result)是否匹配。
14 allure报告结构
@allure.feature('登录注册')
来源:Allure 测试报告框架
作用:将测试用例归类到更高级的功能模块(Feature)中,便于在报告中按业务功能查看测试结果。
参数 '登录注册' 表示该测试属于“登录注册”这一核心功能。
效果:在 Allure 报告中,所有标注了 @allure.feature('登录注册') 的测试会被归拢到一起,方便分析该功能的整体测试覆盖和通过情况。
@allure.story('登录')
来源:Allure 测试报告框架
作用:在某个 Feature 下进一步划分用户故事(Story)或子场景。
参数 '登录' 表示这个测试是“登录注册”功能中“登录”这一具体用户故事的用例。
效果:Allure 报告会以 Feature → Story 的层级结构展示测试用例。例如:
登录注册 (Feature)
└── 登录 (Story)
├── 测试用例1 (参数组1)
├── 测试用例2 (参数组2)
└── ...
更多推荐
所有评论(0)