在对象里也有变量,用来存储数据,这时变量又称字段 (fields)
在对象里也有函数,用来操作数据,这时函数又称方法 (methods)
字段和方法统称为类的属性 (attributes)
本帖的讲述逻辑如下:
第一章先用 Python 里面内置的 int, list, ndarray 和 dataframe 变量举例,感受一下 Python 中万物皆对象,体会一下对象里的属性 (字段和方法)
第二章详细介绍面向对象编程的细节,内容包括:实例变量、类变量、实例方法、类方法、静态方法、继承、多态、魔法方法、属性装饰器等。
第一章 - 对象初体验
1.1 整型 int
1.2 列表 list
1.3 NumPy 数组 - ndarray
1.4 Pandas 数据帧 - dataframe
第二章 - 面向对象编程
2.1 极简类和对象
2.2 init() 和 self
2.3 类变量 (千人千面)
2.4 类变量 (千人一面)
2.5 类方法 + 静态方法
2.6 其他构建函数
2.7 继承和多态
1.1 整形int
这样我们脑海里应该复现这样的类比:
类 : 对象
int : i
i=1024
i.numerator #字段显示1024
i.bit_length()#字段长度的方法,显示为11
1.2列表 list
l = [1, 2, 3]
l.append(4)
l.__getitem__(2) #得到索引对象的值
print(l + l)#[1, 2, 3, 4, 1, 2, 3, 4]
print(l.__add__(l))#[1, 2, 3, 4, 1, 2, 3, 4]
print(l * 2)#[1, 2, 3, 4, 1, 2, 3, 4]
print(l.__mul__(2))#[1, 2, 3, 4, 1, 2, 3, 4]
1.3 NumPy 数组 - ndarray
import numpy as np
arr = np.array( [[1,2,3],[4,5,6],[7,8,9]] )
arr.ndim#2
# 对象.方法()
arr.sum()#45
# 类.方法(对象)
np.sum(arr)#45
print( arr + arr )
print(arr.__add__(arr))
后两行代码的输出结果:
1.4 Pandas 数据帧 - dataframe
import numpy as np
import pandas as pd
df = pd.DataFrame( np.arange(6).reshape(2,3),
columns=list('abc') )
print(df.columns)#Index(['a', 'b', 'c'], dtype='object')
print(df.cumsum())
2.1 极简类和对象
创建一个类,创建两个 Employee 类的对象 emp_1 和 emp_2,打印它们的信息包含对象储存的位置,但也证实对象创建成功,只不过可读性极差 (后面会改进)。
class Employee:
pass
emp_1 = Employee()
emp_2 = Employee()
print(emp_1)#<__main__.Employee object at 0x0000025DEFC05E10>
print(emp_2)#<__main__.Employee object at 0x0000025DEFC05E48>
2.2__init__() 和 self
上节的类里是空的,实际的类是将属性聚集的,用的就是 init 方法。这种将属性聚在一起称作封装 (encapsulation),这是类的第一个特征.
调用 init 方法就是在构建类的实例,即对象.
注意到 init 方法第一个参数永远是 self,表示在创建对象本身;后面的参数都是 Employee 字段 (fields),常见的赋值方式就是
class Employee:
def __init__(self,first,last,pay):
self.first=first
self.last=last
self.pay=pay
def fullname(self):
return '{},{},{}'.format(self.first,self.last,self.pay)
emp_1=Employee('zhang','san','1000')
emp_2=Employee('yang','shan','1000')
print(emp_1.fullname())#zhang,san,1000
print(emp_2.fullname())#yang,shan,1000
print( Employee.fullname(emp_1))#zhang,san,1000
print( Employee.fullname(emp_2))#yang,shan,1000
2.3 类变量 (千人千面)
class Employee:
raise_rate=2.99
def __init__(self,first,last,pay):
self.first=first
self.last=last
self.pay=pay
def fullname(self):
return '{},{}'.format(self.first,self.last)
def apply_raise(self):
self.pay= int(self.pay) *int(self.raise_rate)
emp_1=Employee('zhang','san','100000')
emp_2=Employee('yang','shan','1000')
print(emp_1.pay)
emp_1.apply_raise()
print(emp_1.pay)
如果通过类访问来改变类变量 raise_rate ,那么类和对象下的 raise_rate 值都会变。
如果通过对象 emp_1 访问来改变类变量 raise_rate ,那么只会是对象 emp_1下的 raise_rate 值会变,而类下的和对象 emp_2 的 raise_rate 值不会变。
总结:如果想让类变量千人一面,用
self.类变量
Python 的 self 相当于 C++ 的 this 指针。
2.4 类变量 (千人一面)
增加雇员数目的类变量
class Employee:
raise_rate=2.99
num_of_emps=0
def __init__(self,first,last,pay):
self.first=first
self.last=last
self.pay=pay
Employee.num_of_emps +=1
def fullname(self):
return '{},{}'.format(self.first,self.last)
def apply_raise(self):
self.pay= int(self.pay) *int(self.raise_rate)
print(Employee.num_of_emps)#0
emp_1=Employee('zhang','san','100000')
print(Employee.num_of_emps)#1
emp_2=Employee('yang','shan','1000')
print(Employee.num_of_emps)#2
总结:如果想让类变量千人一面,用
类名.类变量
2.5 类方法 + 静态方法
到目前为止,类里的方法都是实例方法 (instance method),它们都适用于对象。本节介绍类方法 (class method) 和静态方法 (static method)。
class Employee:
raise_rate=2.99
num_of_emps=0
def __init__(self,first,last,pay):
self.first=first
self.last=last
self.pay=pay
Employee.num_of_emps +=1
def fullname(self):
return '{},{}'.format(self.first,self.last)
def apply_raise(self):
self.pay= int(self.pay) *int(self.raise_rate)
@classmethod #类方法
def set_raise_rate(cls,rate):
cls.raise_rate=rate
@staticmethod #静态方法
def is_workday(day):
if day.weekday()==5 or day.weekday()==6:
return False
return True
先讲类方法,类方法适用于该类,即对该类下的所有对象的作用的相同的。类方法有两个特点:
- 第一行要有装饰器 @classmethod (记住就行了)
- 函数第一个参数必须是关键词 clf (对象一个参数必须是关键词 self)
emp_1=Employee('zhang','san','100000')
emp_2=Employee('yang','shan','1000')
print(Employee.raise_rate)#2.99
print(emp_1.raise_rate)#2.99
print(emp_2.raise_rate)#2.99
用类 Employee 来调用类方法,将薪水涨幅调成 1.1,所有用类和对象来访问的 raise_rate 都变成了 1.1。
Employee.set_raise_rate( 1.1 )
print( Employee.raise_rate )#1.1
print( emp_1.raise_rate )#1.1
print( emp_2.raise_rate )#1.1
用对象 emp_1来调用类方法,将薪水涨幅调成 1.2,所有用类和对象来访问的 raise_rate 都变成了 1.2。
emp_1.set_raise_rate( 1.2 )
print( Employee.raise_rate )#1.2
print( emp_1.raise_rate )#1.2
print( emp_2.raise_rate )#1.2
小结:类方法是所有对象和类都能调用,而且产生的效果是一样。
再讲静态方法
一个类还会有些效用函数 (utility function),它们不随对象和类的属性而改变,因此我们称它们为静态方法。
静态方法也有两个特点:
- 第一行要有装饰器 @staticmethod (记住就行了)
- 函数参数绝对不能有关键词 clf 和 self
import datetime
my_date = datetime.date(2019, 11, 1)
print( Employee.is_workday(my_date) )
#True
2.6 其他构建函数
def from_string(cls,emp_str):
first,last,pay=emp_str.split('-')
return cls(first,last,pay)
emp_str_1='Steven-zhang-2000000'
emp_1=Employee.from_string(emp_str_1)
print(emp_1)
2.7 继承和多态
继承 (inheritance) 是类的一大特征。有继承就必有父类 (parent class) 和子类 (child class)。
下面构建雇员 Employee 的两个子类,开发者 Developer 和经理 Manager。由父类衍生出来多个子类称为多态 (polymorphism)。
子类Developer
class Developer(Employee):
pass
dev_1=Developer('steven','wang','2000000')
print(dev_1.email)#steven.wang@gmail.com
虽然在 Develop 子类里没有定义任何操作,但是他们继承了父类 Employee 里面的邮箱地址的字段。
如果在子类中更改父类的字段,那么不再使用父类的字段。
现在完善子类 Developer,但是 init 方法里面的代码重复。那么怎么办呢?可以用super方法
以上的super方法让其更简化。输出结果是:
子类manager
class Employee:
raise_rate=2.99
num_of_emps=0
def __init__(self,first,last,pay):
self.first=first
self.last=last
self.pay=pay
self.email=first+'.'+last+'@gmail.com'
def fullname(self):
return '{},{}'.format(self.first,self.last)
def apply_raise(self):
self.pay= int(self.pay*self.raise_rate)
class Developer(Employee):
def __init__(self,first,last,pay,prog_lang):
super().__init__(first,last,pay)
self.prog_lang=prog_lang
self.email=first+'.'+last+'@gmail'
class Manager(Employee):
def __init__(self,first,last,pay,employees=None):
super().__init__(first,last,pay)
if employees==None:
self.employees==[]
else:
self.employees=employees
def add_emp(self,emp):
if emp not in self.employees:
self.employees.append(emp)
def remove_emp(self,emp):
if emp in self.employees:
self.employees.remove(emp)
def print_emp(self):
for emp in self.employees:
print('-->',emp.fullname())
dev_1=Developer('steven','wang',1000000,'py')
dev_2=Developer('LILY','yang',20000000,'java')
mgr_1=Manager('Jack','Black',500000,[dev_1])
print(mgr_1.email)
mgr_1.print_emp()
两种检查函数
-函数 isinstance(a, A) 是检查 a 是不是 A 的一个实例。
- 函数 issubclass(A, B) 是检查 A 是不是 B 的一个子类。
形如,print( issubclass(Manager, Employee) ),结果为True 或 False
多态:不同对象对同一方法响应不同的行动
class Animal:
def run(self):
raise AttributeError('子类必须实现这个方法')
class People(Animal):
def run(self):
print('人正在走')
class Pig(Animal):
def run(self):
print('pig is walking')
class Dog(Animal):
def run(self):
print('dog is running')
def func(animal):
animal.run()
func(Pig())
# pig is walking