CSU East Bay logo

Chemistry 311

User Defined Functions

There are three levels of availability for functions in Python.

Defining Your Own Functions

So far, we have used functions that were built into Python or imported from libraries such as math. As programs become larger, however, it is often useful to write your own functions. A function groups together a sequence of statements that perform a specific task. Once a function has been defined, it can be called as many times as needed throughout a program.

A function definition begins with the def keyword, followed by the function's name and a pair of parentheses. If the function requires information to perform its task, one or more parameters are listed inside the parentheses. Parameters are variables that receive values when the function is called. The first line of the function definition ends with a colon (:), and every statement that belongs to the function must be indented.


    def function_name(parameter1, parameter2):
        ...
        ...
        return value
    

The statements represented by the ellipses (...) are the body of the function. They perform whatever calculations or operations are required. Many functions end with a return statement, which sends a value back to the part of the program that called the function. The returned value can be stored in a variable, used in an expression, or passed directly to another function.

Big idea: Functions allow you to divide a large programming problem into smaller, self-contained pieces. Well-designed functions make programs easier to read, easier to test, and easier to reuse.

Worked examples

Worked Example: Numerical Differentiation Using Functions

One advantage of writing your own functions is that they can be reused in other parts of a program. In this example, we will define one function that calculates a chemical rate constant using the Arrhenius equation, and a second function that estimates the derivative of any function using a simple numerical approximation.

Suppose the rate constant follows the Arrhenius equation

\[ k=Ae^{-E_a/(RT)} \]

where \(A=1.00\times10^{13}\ {\rm s^{-1}}\), \(E_a=75.0\ {\rm kJ/mol}\), and \(R=8.314\ {\rm J\,mol^{-1}\,K^{-1}}\).


      from math import exp

      def rate_constant(T):
          A = 1.0e13
          Ea = 75000.0
          R = 8.314

          return A * exp(-Ea / (R * T))


      def derivative(F, x):
          return (F(1.01 * x) - F(x)) / (0.01 * x)


      T = 298.15

      k = rate_constant(T)
      dkdT = derivative(rate_constant, T)

      print(f"T = {T:.2f} K")
      print(f"k = {k:.3e} s^-1")
      print(f"dk/dT = {dkdT:.3e} s^-1 K^-1")
      

Output


      T = 298.15 K
      k = 7.238e-01 s^-1
      dk/dT = 7.360e-02 s^-1 K^-1
      

Discussion

The function rate_constant() calculates the Arrhenius rate constant for any temperature. Instead of writing the Arrhenius equation every time we need it, we simply call rate_constant(T).

The second function, derivative(F, x), is much more general. Instead of accepting a number as its first parameter, it accepts another function. The parameter F can represent any function that accepts one argument.

The derivative is estimated by evaluating the function at two nearby values of the independent variable:


      (F(1.01 * x) - F(x)) / (0.01 * x)
      

This expression approximates the slope of the function using a small forward difference. Because the two points are very close together (only 1% apart), the estimated slope is usually very close to the true derivative.

Since derivative() accepts a function as an argument, it is not limited to the Arrhenius equation. It can be used to estimate the derivative of any single-variable function.


      dkdT = derivative(rate_constant, 298.15)
      

Here, the function rate_constant (notice there are no parentheses) is passed to derivative(). The derivative function then evaluates rate_constant() internally at two nearby temperatures to estimate the slope.

Note

This is called numerical differentiation. It provides an approximation to the true derivative, and its accuracy depends on how small the step size is. Choosing a smaller step generally improves the approximation, although making the step too small can introduce roundoff errors due to finite numerical precision.

Additional Note

One subtle but important detail is that the function name rate_constant is passed to derivative() without parentheses. Writing rate_constant passes the function itself, allowing derivative() to decide when and how many times to evaluate it. If we instead wrote rate_constant(298.15), Python would immediately calculate the rate constant at 298.15 K and pass the resulting number to derivative(). Since the derivative function needs to evaluate the rate constant at two nearby temperatures, it must receive the function itself, not the value returned by the function.

Key points (one glance)

Big picture: Functions make programs modular. By writing small, well-defined functions that perform individual tasks, you can build larger scientific programs that are easier to understand, test, reuse, and maintain. Passing functions as arguments allows you to write general-purpose numerical algorithms that work with many different mathematical models.