Basic Concept of Numbers as Objects
Every Python number is an object with a numeric type. That type determines which attributes and methods are available in addition to arithmetic operators.
For example, creates an int and creates a float. Numeric objects are immutable, so their methods return information or new values rather than changing the original number.
Accessing Attributes and Methods
Read an attribute with object.attribute. Call a method with object.method(), including the parentheses that perform the call.
Let's look at a practical example with complex numbers:
# Creating a complex number
c = 2 + 3j
# Accessing real attribute (real part)
print(c.real) # Output: 2.0
# Accessing imag attribute (imaginary part)
print(c.imag) # Output: 3.0
# Calling conjugate() method to get conjugate
print(c.conjugate()) # Output: (2-3j)Identifying the Numeric Type
Before using a type-specific operation, you can inspect which numeric type a value has.
Using the type() Function
The type() function returns the value's class:
# Viewing number data types
print(type(42)) # Output: <class 'int'>
print(type(3.14)) # Output: <class 'float'>
print(type(2+3j)) # Output: <class 'complex'>Complex Number Attributes
A complex number exposes its two components through the real and imag attributes. Both are returned as floating-point values.
The real Attribute
The real attribute returns the real component:
# Example usage of real attribute
z1 = 4 + 5j
z2 = -2 + 7j
z3 = 10 + 0j # Pure real number
print(f"Real part of {z1} is {z1.real}")
print(f"Real part of {z2} is {z2.real}")
print(f"Real part of {z3} is {z3.real}")
# Output:
# Real part of (4+5j) is 4.0
# Real part of (-2+7j) is -2.0
# Real part of (10+0j) is 10.0The imag Attribute
The imag attribute returns the coefficient of . Python writes that unit as j in complex literals.
# Example usage of imag attribute
z1 = 3 + 8j
z2 = 6 - 4j
z3 = 0 + 9j # Pure imaginary number
print(f"Imaginary part of {z1} is {z1.imag}")
print(f"Imaginary part of {z2} is {z2.imag}")
print(f"Imaginary part of {z3} is {z3.imag}")
# Output:
# Imaginary part of (3+8j) is 8.0
# Imaginary part of (6-4j) is -4.0
# Imaginary part of 9j is 9.0Complex Number Methods
The conjugate() Method
For , conjugate() returns . The real part stays fixed while the sign of the imaginary part changes.
# Example usage of conjugate() method
z1 = 3 + 4j
z2 = -2 - 5j
z3 = 7 + 0j
print(f"Conjugate of {z1} is {z1.conjugate()}")
print(f"Conjugate of {z2} is {z2.conjugate()}")
print(f"Conjugate of {z3} is {z3.conjugate()}")
# Output:
# Conjugate of (3+4j) is (3-4j)
# Conjugate of (-2-5j) is (-2+5j)
# Conjugate of (7+0j) is (7-0j)Practical Use of Conjugate
The identity connects a complex number to its modulus. The example shows that relationship explicitly; in ordinary Python code, abs(z) is the direct way to compute the modulus.
import math
# Calculating complex number modulus using conjugate
z = 3 + 4j
modulus_squared = z * z.conjugate()
modulus = math.sqrt(modulus_squared.real)
print(f"Complex number: {z}")
print(f"Its conjugate: {z.conjugate()}")
print(f"z × z*: {modulus_squared}")
print(f"Modulus |z|: {modulus}")
print(f"Built-in abs(z): {abs(z)}")
# Output:
# Complex number: (3+4j)
# Its conjugate: (3-4j)
# z × z*: (25+0j)
# Modulus |z|: 5.0
# Built-in abs(z): 5.0Attributes and Methods on Other Numbers
Integers and floating-point numbers also expose methods suited to their representations.
Integer Numbers
# Some methods on integer numbers
n = 42
# bit_length() method - calculates the number of bits needed
print(f"Number of bits for {n}: {n.bit_length()}")
# to_bytes() method - converts to bytes
byte_representation = n.to_bytes(2, byteorder='big')
print(f"Byte representation of {n}: {byte_representation}")
# Output:
# Number of bits for 42: 6
# Byte representation of 42: b'\x00*'bit_length() counts the bits needed for the absolute integer value, excluding its sign and leading zeros. to_bytes() additionally requires an output length and byte order, so the two-byte result above begins with a zero byte.
Float Numbers
# Some methods on float numbers
f = 3.14159
# is_integer() method - checks if float is a whole number
print(f"{f} is a whole number: {f.is_integer()}")
print(f"{4.0} is a whole number: {(4.0).is_integer()}")
# as_integer_ratio() method - returns ratio as a fraction
ratio = f.as_integer_ratio()
print(f"Ratio of {f}: {ratio}")
# Output:
# 3.14159 is a whole number: False
# 4.0 is a whole number: True
# Ratio of 3.14159: (3537115888337719, 1125899906842624)is_integer() checks whether a finite float has no fractional part. as_integer_ratio() returns the exact fraction represented by the stored binary float, which may differ from the decimal fraction a reader expects.
Practical Applications in Programming
Data Validation
Attributes can support explicit validation rules. The following function requires the input to be a complex object, then applies two example domain rules to its components.
def validate_complex_input(z):
"""Function to validate complex number input"""
if not isinstance(z, complex):
return False, "Input is not a complex number"
if z.real < 0:
return False, "Real part cannot be negative"
if z.imag == 0:
return False, "Imaginary part cannot be zero"
return True, "Valid input"
# Testing validation function
test_numbers = [3+4j, -2+5j, 7+0j, 2.5+1.5j]
for num in test_numbers:
is_valid, message = validate_complex_input(num)
print(f"{num}: {message}")Mathematical Calculations
def complex_operations(z1, z2):
"""Performing various operations on complex numbers"""
print(f"Number 1: {z1}")
print(f"Number 2: {z2}")
print(f"Real part of z1: {z1.real}")
print(f"Imaginary part of z1: {z1.imag}")
print(f"Conjugate of z1: {z1.conjugate()}")
# Mathematical operations
addition = z1 + z2
multiplication = z1 * z2.conjugate()
print(f"z1 + z2 = {addition}")
print(f"z1 × z2* = {multiplication}")
# Usage example
z1 = 2 + 3j
z2 = 1 - 2j
complex_operations(z1, z2)The useful pattern is to identify the numeric type, choose the operation owned by that type, and remember whether it returns a component, a representation, or a new value. That distinction becomes increasingly important when numerical code grows beyond a single expression.