skip to content

Why does Selenium's Python client write find_element(By.ID, "x") where Java writes findElement(By.id("x"))?

level: middleimportance: should knowfreq 55%

answer

  1. Both carry a strategy and a value
  2. One binding packs them, one keeps them apart
  3. Ask what type By.ID actually is
  4. A Python locator is stored as a tuple
  5. By.id() builds an object; By.ID names a strategy

basics

~20 s

Java's By is a factory that returns one locator object holding both the strategy and the value. Python's By is a set of strategy constants, so the strategy and the value stay two separate arguments.

solid answer

~40 s

Both calls carry the same two facts - the strategy and the value - and differ only in packaging. In Java, `By` is an abstract class whose static methods return a `By` object combining both, so one argument is enough and the object can be stored in a field: `private static final By TERM_AVERAGE = By.id("term-average")`. In Python, `By` just holds constants such as `By.ID` and `By.CSS_SELECTOR` that name the strategy, and `find_element(by, value)` takes them apart. That is why a Python page module stores a locator as a tuple, `(By.ID, "term-average")`, and unpacks it with `find_element(*TERM_AVERAGE)`. In Selenium 4.3 the older `find_element_by_id` helpers were removed, so the two-argument form is the only spelling.

go deeper

for a junior

Recall the two spellings and use each correctly: a factory call in Java, a constant plus a value in Python. Do not try to call the Python constant like a method.

for a middle

Explain what each By actually is - an abstract class with static factories against a holder of strategy constants - and what that means for how a page module stores its locators.

for a senior

Show you can spot the age of a sample from its locator style, and that you would standardise how locators are declared in a suite so helpers and condition calls agree on the shape.

for a principal

Weigh what a multi-binding codebase costs when even the locator declaration differs, and decide whether shared selector constants or duplicated page modules is the cheaper long-term arrangement.

## Two designs for the same pair of values Every element lookup carries exactly two pieces of information: **which strategy** to search by (id, css selector, xpath) and **what value** to search for. The bindings disagree only about how those two pieces are packaged before they reach the client's find call. - **Java** packs them into a single object with a static factory: `By.id("term-average")`. - **Python** keeps them separate: `find_element(By.ID, "term-average")`, where `By.ID` is a constant. - **Ruby** merges them into a keyword argument: `find_element(id: "term-average")`. - **C#** follows Java: `FindElement(By.Id("term-average"))`. - **JavaScript** follows Java too: `findElement(By.id('term-average'))`, awaited. Nothing about this changes the lookup itself. A report card's average grade is found the same way in all five; the argument list is a local-language decision, not a behavioural one. ## What Java's By is `org.openqa.selenium.By` is an abstract class whose static methods - `id`, `name`, `className`, `tagName`, `linkText`, `partialLinkText`, `cssSelector`, `xpath` - each return a `By` instance holding both halves. Because it is an object, it is a **value you can store, pass and reuse**: ```java private static final By TERM_AVERAGE = By.id("term-average"); private static final By GRADE_ROWS = By.cssSelector(".grade-row"); ``` Anything in the Java client that accepts a locator accepts that one object - `findElement`, `findElements`, and the `ExpectedConditions` factories alike. That uniformity is why a Java sample never shows a locator being split apart. ## What Python's By is `selenium.webdriver.common.by.By` is a holder of **string constants** - `By.ID`, `By.NAME`, `By.CLASS_NAME`, `By.TAG_NAME`, `By.LINK_TEXT`, `By.PARTIAL_LINK_TEXT`, `By.CSS_SELECTOR`, `By.XPATH` - whose values are the strategy names the WebDriver protocol uses, such as `css selector` and `partial link text`. They are not factories: `By.ID("term-average")` is a mistake, not an alternative spelling. The client's signature is `find_element(by, value)`, so the two halves stay two arguments. Two consequences follow directly: - **Stored locators are tuples.** A Python page module writes `TERM_AVERAGE = (By.ID, "term-average")` and then unpacks it: `driver.find_element(*TERM_AVERAGE)`. - **Helpers that take one locator need the tuple as-is.** The `expected_conditions` functions take a single locator argument, so the pair is passed as `visibility_of_element_located((By.ID, "term-average"))` - the double parentheses that every Python newcomer forgets once. ## Reading a sample in any binding | Binding | Locator spelling | What the locator *is* | |---|---|---| | Java | `By.cssSelector(".grade-row")` | an object carrying strategy and value | | Python | `By.CSS_SELECTOR, ".grade-row"` | a constant plus a separate value | | C# | `By.CssSelector(".grade-row")` | an object carrying strategy and value | | Ruby | `css: ".grade-row"` | a hash key naming the strategy | | JavaScript | `By.css('.grade-row')` | an object carrying strategy and value | The JavaScript row hides a small trap for a Java reader: the method there is `By.css`, not `By.cssSelector`. The Python constant is `By.CSS_SELECTOR`. Same strategy, three names. ## The Selenium 4 detail worth knowing Older Python samples call `driver.find_element_by_id("term-average")` and a family of `find_element_by_*` siblings. Those were deprecated at **Selenium 4.0** and **removed in 4.3**, so the two-argument `find_element(By.ID, ...)` form is not one option among several - it is the spelling. When a sample you are handed uses the old helpers, you are reading pre-4.3 code, which is a useful dating clue for everything else in it. ## What the packaging does not change It is worth being explicit about what a locator's packaging does **not** affect, because the difference in argument count looks more significant than it is: - The strategy is the same one either way. `By.CSS_SELECTOR` in Python and `By.cssSelector` in Java name the same kind of lookup, and the selector string itself is identical character for character. - The amount of work is the same. Nothing about a tuple or an object makes a lookup cheaper or dearer than the equivalent line in another binding. - The failure is the same event, reported through that language's exception type and message formatting rather than through a different mechanism. So when a Java report-card test and its Python port disagree, the locator packaging is almost never the cause. The selector string, or the timing around it, is - and the packaging is just the part that made the two files look unlike each other. ## What to do with this when you read code 1. Find the client's find call and count its arguments; that tells you which packaging the binding uses. 2. Look at how locators are stored at the top of a page module - a `By` field, a tuple, or a hash - and you will know what every helper below it expects. 3. When the same pair appears as two arguments in one line and one parenthesised pair in the next, you are in Python and the second call is a condition helper, not a different kind of lookup.

  • Why can a Python locator be stored as a tuple but a Java locator cannot be stored as a string?
    Because Java's locator is a `By` object that carries the strategy and the value together and is what the API accepts; a string would only be half of it. Python's API takes the two halves separately, so a two-element tuple is exactly the right container and unpacks straight into the call.
  • How does the Ruby keyword form relate to the Python two-argument form?
    It is the same split with different syntax. Ruby's `find_element(id: "term-average")` uses the hash key as the strategy and the value as the target, so a Ruby locator is also two pieces rather than an object. Ruby additionally accepts the positional pair, `find_element(:id, "term-average")`.
  • What is the JavaScript client's name for the css strategy?
    `By.css(...)`, not `By.cssSelector(...)` as in Java, and not the `By.CSS_SELECTOR` constant of Python. The C# spelling is `By.CssSelector(...)`. It is the same strategy under four names, and one of the easier things to get wrong when copying a locator between samples.

saying these in an interview costs you the question

  • Calls By.ID("term-average") in Python as if it were a factory
  • Thinks By.ID is a class you have to instantiate
  • Uses find_element_by_id, removed from the Python client in 4.3
  • Stores a Python locator as a By object instead of a tuple
  • Assumes the argument count changes what the browser is asked