《流畅的Python》第一个例子中的陷阱

《流畅的Python》书中首个例子中,作者使用了类变量和实例变量的方式,可能导致混淆。当在方法中引用未定义的实例变量时,Python会查找并使用同名类变量。为保持语义清晰,应明确使用类变量。通过在__init__()方法中输出self.suits和self.ranks的id值,可以验证它们是类变量,所有对象共享。如果将suits和ranks改为实例变量,每个对象将拥有独立的内存空间。当类和实例变量同名时,实例变量会屏蔽类变量,要引用类变量需通过类名前缀。

《流畅的Python》是一本不可多得的好书,然而不知是有意或出于无意,作者在书中留下了一些陷阱,让我们从全书的第一个例子说起。

下面是本书第一个例子的源码:

import collections
Card=collections.namedtuple('Card',['rank','suit'])

class FrenchDeck:
    ranks=[str(n) for n in range(2,11)] + list('JQKA')  #1
    suits='spades diamonds clubs hearts'.split()   #2

    def __init__(self):
        self._cards=[Card(rank,suit) for suit in self.suits
                     for rank in self.ranks]    #3

    def __len__(self):
        return len(self._cards)

    def __getitem__(self,position):
        return self._cards[position]

整个例子短小紧凑,但是仔细端详,会发现#1和#2两句声明的是两个类变量,而#3句却是用实例变量的方式来引用之。

从Python默认的行为看,这种情况是允许的。就是说,如果在方法中引用了某个没有定义的实例变量,那么会去查找同名的类变量,如果找到,则会获取同名类变量的值,否则引发异常。不信,你把ranks和suits两个类变量删掉试试,再次创建FrenchDeck类的对象,Python马上给你脸色看。

所以为了保持语义上的一致,#3一句如下书写更为清晰准确:

 self._cards=[Card(rank,suit) for suit in FrenchDeck.suits
                     for rank in FrenchDeck.ranks]  #3

但是,这个问题还没有结束,我们接着往下看。

回到原程序,如何确认__init__()方法中引用的是类变量呢?

方法很简单,我们在__init__()函数中的#3句之后加上一句代码,使得现在的__init__()函数看起来是这样的:

 def __init__(self):
        self._cards=[Card(rank,suit) for suit in self.suits
                     for rank in self.ranks]    #3
        print(id(self.suits))

按F5,在打开的python shell中,我们创建两个FrenchDeck类的变量:

>>>deck1=FrenchDeck()
>>>deck2=FrenchDeck()

因为我们在__init__()函数中,添加了输出self.suits的id值,所以创建FrenchDeck类的对象时就会打印这个值,结果deck1和deck2对象打印出的值相等。这就对了,类变量只创建一次,并且在所有对象中共享。

同样,可以确认ranks变量也是类变量。

还有疑问,对吗?好的,程序员就是要有探究到底的精神,我们再来做一个实验,把源代码改为如下:

import collections
Card=collections.namedtuple('Card',['rank','suit'])

class FrenchDeck:
    def __init__(self):
        self.ranks=[str(n) for n in range(2,11)] + list('JQKA')  #1
        self.suits='spades diamonds clubs hearts'.split()   #2
        self._cards=[Card(rank,suit) for suit in self.suits
                     for rank in self.ranks]    #3
        print(id(self.suits))
         
    def __len__(self):
        return len(self._cards)

    def __getitem__(self,position):
        return self._cards[position]

#1和#2两句彻底把suits和ranks声明为实例变量,我们再创建FrenchDeck类的两个对象,来检查suits的id值,这次发现两个对象中的suits的id值不一样了。因为每个对象都为各自的实列变量开辟内存空间。同样也可以通过检查ranks的id值来确认这一点。

最后一个问题,如果在一个类的定义中,出现同名的类变量和实列变量,那情况又会如何呢?

做个试验就可以搞定这个问题,我们把源码改为如下:

import collections
Card=collections.namedtuple('Card',['rank','suit'])

class FrenchDeck:
    ranks=[str(n) for n in range(2,11)] + list('JQKA')     
    suits='spades diamonds clubs hearts'.split()   

    def __init__(self):
        self.ranks=[str(n) for n in range(10)]
        self.suits=[str(n*2) for n in range(10)]
        
        self._cards=[Card(rank,suit) for suit in self.suits
                     for rank in self.ranks]
        print(self.suits)
        
    def __len__(self):
        return len(self._cards)

    def __getitem__(self,position):
        return self._cards[position]

F5运行后,创建FrenchDeck类的实列,这时候输出的是:

['0', '2', '4', '6', '8', '10', '12', '14', '16', '18']

原来,实列变量可以屏蔽同名的类变量,那么此时我们需要引用类变量怎么办呢?简单,直接在类变量前冠以类名即可。

<think>嗯,用户问的是如何在Python的Selenium库中点击表格的第一行。首先,我需要确定用户对Selenium的熟悉程度。可能他们刚开始用,所以需要一步步引导。 首先,用户需要定位到表格。表格通常用<table>标签,但具体结构可能不同。常见的定位方式有ID、Class或XPath。我应该建议先检查页面结构,用开发者工具查看表格的HTML。 然后,找到表格中的第一行。行一般是<tr>标签。可能需要用XPath来定位第一个tr元素。比如,用//table[@id='tableId']//tr[1],但要注意索引可能从1开始,也可能有其他行比如表头,需要根据实际情况调整。 接下来是点击操作。找到元素后,调用click()方法。但需要注意元素是否可点击,有时候可能需要等待元素加载,或者处理异常,比如元素被遮挡、不在可视区域等。 可能的陷阱包括动态内容加载,导致元素未及时出现。这时候可能需要显式等待,比如WebDriverWait配合expected_conditions。还有可能第一行内有具体的元素需要点击,比如按钮或链接,这时候可能需要进一步定位到子元素。 用户可能需要具体的代码示例,所以应该提供一个例子,涵盖定位表格、获取第一行、点击的步骤,并提醒异常处理和等待的必要性。此外,建议他们根据实际HTML调整选择器,比如更换ID或Class。 总结步骤:检查表格结构→定位表格→定位第一行→执行点击→处理可能的问题。同时要提醒常见问题,比如索引错误、元素不可交互等,帮助他们调试。</think>在Python中使用Selenium点击表格第一行的完整步骤如下: 1. **定位表格元素** 首先需要确定目标表格的定位方式,建议按优先级选择: ```python # 通过ID定位(最精准) table = driver.find_element(By.ID, "table_id") # 通过XPath定位(通用性强) table = driver.find_element(By.XPATH, "//table[@class='data-table']") ``` 2. **定位首行元素** 获取表格第一行通常有两种方式: ```python # 方式1:直接通过tr选择器定位第一个子元素 first_row = table.find_element(By.CSS_SELECTOR, "tr:first-child") # 方式2:使用XPath索引定位 first_row = table.find_element(By.XPATH, ".//tr[1]") ``` 3. **执行点击操作** ```python first_row.click() ``` ⚠️ **常见问题处理方案:** - **元素不可点击**:添加显式等待确保元素可交互 ```python from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC row = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, "//tr[1]")) ) row.click() ``` - **动态加载内容**:添加强制等待(临时方案) ```python import time time.sleep(2) # 等待2秒确保内容加载 ``` - **元素被遮挡**:使用JavaScript直接点击 ```python driver.execute_script("arguments[0].click();", first_row) ``` 4. **完整示例代码** ```python from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC driver = webdriver.Chrome() driver.get("https://example.com/table-page") try: # 等待表格加载 table = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "data-table")) ) # 获取首行并点击 first_row = table.find_element(By.CSS_SELECTOR, "tr:first-child") first_row.click() print("成功点击表格首行") except Exception as e: print(f"操作失败:{str(e)}") finally: driver.quit() ``` 💡 **调试建议:** 1. 使用`print(first_row.get_attribute('outerHTML'))`输出元素HTML确认定位准确 2. 在点击前添加滚动操作确保元素可见 ```python driver.execute_script("arguments[0].scrollIntoView();", first_row) ``` 3. 检查是否存在`<iframe>`,需要先切换frame才能操作内部元素 根据实际网页结构,可能需要调整定位策略。如果表格有表头(`<thead>`)和数据行(`<tbody>`)的分隔,建议使用更精确的定位: ```python # 定位数据区域的第一行 data_row = table.find_element(By.XPATH, ".//tbody/tr[1]") ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

kingdragonfly

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

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

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

打赏作者

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

抵扣说明:

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

余额充值