[Python] Object-Oriented Programming

Class

1
2
3
4
5
6
7
8
9
10
11
12
13
# Define a class
class MyClass:
def __init__(self, name):
self.val = name

def __call__(self, x, y):
print("Hello: {0}".format(self.value))
sum = x + y
print("The sum is {0}".format(sum))
return sum

foo = MyClass('brad')
foo(2,9) # By using __call__ , we can treat this instance of the class just like a function

Class Variables

Class Variables should be the same for every instances. self is a special parameter that will always refer to the object itself.

Restrict access to class variables: start variable name with __

1
2
3
4
5
6
7
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score

def print_score(self):
print('%s: %s' % (self.__name, self.__score))

You can’t get access to private variables now:

1
2
3
4
5
>>> bart = Student('Bart Simpson', 59)
>>> bart.__name
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute '__name'

To get and edit private variables:

1
2
3
4
5
6
7
8
9
10
11
class Student(object):
...

def get_name(self):
return self.__name

def get_score(self):
return self.__score

def set_score(self, score):
self.__score = score

Use this method, we can do a validation before setting values to class variables.

一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。

不能直接访问name是因为Python解释器对外把`name变量改成了_Studentname,所以,仍然可以通过_Studentname来访问__name`变量:

1
2
>>> bart._Student__name
'Bart Simpson'

Static Methods and Class Methods

Class methods provides alternative way to declare an instance of the class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Employee:
num_of_emps = 0
raise_amt = 1.04

def __init__(self, first, last, pay):
self.first = first
self.last = last
self.pay = pay

Employee.num_of_emps += 1 # Update class variable

@classmethod
def set_raise_amt(cls, amount):
cls.raise_amt = amount

@staticmethod
def is_workday(day):
if day.weekday() == 5 or day.weekday() == 6:
return False
return True


Employee.set_raise_amt(1.05) # Use class method

emp_1 = Employee('Corey', 'Schafer', 50000)
emp_1.set_raise_amt(1.05)

import datetime
my_date = datetime.date(2016, 7, 10)
print(Employee.is_workday(my_date))

Inheritance

Method resolution order: how python look for methods and attributes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Developer(Employee): # developer class will have all attributes and methods of Employee class
raise_amt = 1.10
def __init__(self, first, last, pay, prog_lang):
super().__init__(first, last, pay) # Inheritance from parent class
self.prog_lang = prog_lang

class Manager(Employee):
def __init__(self, first, last, pay, employee=None):
super().__init__(first, last, pay)
if employees is None:
self.employees = []
else:
self.employees = employees

def add_emp(self, emp):
if emp in self.employees:
self.employees.append(emp)

dev_1 = Developer('Corey', 'Schafer', 50000, 'Python')
print(issubclass(Developer, Employee))

Object

1
2
3
4
5
a = 1
def fun(a):
a = 2
fun(a)
print(a) # 1

Explanation: https://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference

Immutable Object string, tuples, numbers
If you pass a immutable object into a method, you still can’t rebind the outer reference, and you can’t even mutate the object.

Mutable Object list, dict, set
When you pass a mutable object into a method, the method gets a reference to that same object and you can mutate it to whatever, but if you rebind the reference in the method, the outer scope will know nothing about it, and after you’re done, the outer reference will still point at the original object.

Method

There’re three types of methods in python: staticmethod, classmethod, and instancemethod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def foo(x):
print("executing foo(%s)" % x)

class A(object):
def foo(self, x): #instance method: have to bind to self(instance)
print("executing foo(%s)" % self, x)

@classmethod: # class method: have to bind to cls(class)
def class_foo(cls, x):
print("executing foo(%s, %s)" % (cls, x))

@staticmethod: # static method: similar to normal method, a.static_foo(x)
def static_foo(x):
print("executing foo(%s)" % x)

a = A()
foo(x) # normal method
a.foo(x) # instance method
A.class_foo(x) # class method
a.class_foo(x)
A.class_foo(x)
a.static_foo(x)

self and cls is the binding of instance and class.

[Difference between static method and class method] (https://stackoverflow.com/questions/136097/what-is-the-difference-between-staticmethod-and-classmethod)

For staticmethod, neither self nor cls will be implicitly passed as the first argument. They behave like plain functions except that you can call them from an instance or the class.

Class variable and instance variable

Class variable Shared by all the instance.

1
2
3
4
5
6
7
8
9
10
11
class Test(object):
num_of_instance = 0
def __init__(self, name):
self.name = name
Test.num_of_instance += 1

if __name__ == '__main__':
print(Test.num_of_instance) # 0
t1 = Test('jack')
print(Test.num_of_instance) # 1
t2 = Test('lucy')
1
2
3
4
5
6
7
8
9
class Person:
name = "aaa"
p1 = Person()
p2 = Person()

p1.name = "bbb" # Changed the reference of instance
print(p1.name) # bbb
print(p2.name) # aaa
print(Person.name) # aaa

Polymorphism

Great Video: https://www.youtube.com/watch?v=qqYOYIVrso0

在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是,反过来就不行

1
2
3
>>> b = Animal()
>>> isinstance(b, Dog)
False

对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。

对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了:

1
2
3
class Timer(object):
def run(self):
print('Start...')

这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。