1.1 面向对象
三个基本特性:
- 封装性(隐藏了内部细节,只保留有限的对外接口)
- 继承性(代码的复用,父类(一般类,超类),子类(特殊类,派生类))
- 多态性(子类继承父类,具有不同的状态或表现行为,即重写父类方法)
1.2 类和对象
类就是数据类型,封装了一类对象的数据和操作。
1.2.1 定义类
class 类名 (父类):
类体
类名首字母大写,父类可以省略(默认继承object类)
class Animal(object):
pass
class Animal: # 可以省略父类
pass
1.2.2 创建对象
类实例化可生成对象,对象也称为实例。对象的生命周期包括创建,使用,销毁。由于有垃圾回收,所以一般不需要负责对象的销毁。
对象名 = 类(参数...)
class Animal(object):
pass
a = Animal()
print(a) # <__main__.Animal object at 0x0000021A2F913190>
打印的信息是调用了对象的__str()__
方法,如果想要打印自己的信息,可以重写这个方法。
class Animal(object):
def __str__(self):
return "我是基类Animal对象"
a = Animal()
print(a)
1.2.2 类的成员
类成员包括:
- 成员变量
- 实例(对象)变量
- 类变量
- 属性:
- 属性指的是attribute(类中保存的数据变量)
- getter,setter读写访问器,对于读写访问器python提供了property实现。
- 有些书中把attribute称为成员变量或字段,把property称为属性
- 成员方法
- 实例方法
- 类方法
- 静态方法
注意:很多书中写的变量,属性,字段都是同一个事物的不同叫法。
1.2.3 实例变量
实例变量就是每个实例特有的数据。
class Animal(object):
def __init__(self,name,sex,age):
self.name = name # 定义实例变量:self.name
self.sex = sex
self.age = age
def __str__(self):
return "name:{}, sex:{}, age:{}".format(self.name,self.sex,self.age)
a = Animal("小白",'男',2)
print(a)
其中:__init__(self)
是构造函数,用于初始化类的,这个方法名称是固定的。
self
放在第一个位置,表示当前类的对象,就是当前正在创建的对象。在创建类的实例对象时不需要手动指定。
self.name
表示对象中的实例变量,即self对象里的变量。
1.2.3.1 访问实例变量
# 读:对象名.实例变量
# 写:对象名.实例变量 = 值
a.name = "小黑"
print(a.name)
1.2.4 类变量
类变量是属于类本身的,所有实例对象共享这个变量。
- 定义位置在类中(不在构造函数中)
- 访问:类名.类变量名
class Animal(object):
count = 0 # 在这儿定义,不要在构造函数中定义
def __init__(self):
Animal.count += 1
a = Animal()
print(Animal.count) # 1
b = Animal()
print(Animal.count) # 2
注意:
不要用 对象名.类变量
这种方式访问:
- 它会先在实例中查找这个变量,如果没找到才去类命名空间中去查找。
- 如果赋值则是创建对象的变量,而不会对类变量进行赋值。这是因为python是动态语言,可以用对象名.实例变量名来添加当前对象的实例变量。
1.2.5 实例方法
实例方法和实例变量一样,都是针对对象本身的。
创建实例方法:
class 类:
def 方法名(self, 参数...):
pass
其中self,是用于方法和实例的绑定。
调用实例方法:对象名.实例方法名(参数...)
class Animal(object):
def __init__(self, age):
self.age = age
def age_add(self): # 定义实例方法
self.age += 1
a = Animal(3)
print(a.age) # 3
a.age_add() # 调用实例方法
print(a.age) # 4
1.2.6 类方法
类方法属于类,不与实例绑定,但需要与类绑定。定义的第一个参数不是self,而是类的type实例,type是描述数据类型的类。python中所有的数据类型都是type的一个实例。
定义与访问:
class 类:
@classmethod
def 类方法名(cls, 参数...)
在类的内部访问类变量和类方法:
cls.类变量
cls.类方法()
pass
访问:
类.类方法名(参数)
其中 @classmethod
是固定的,使用装饰器声明该方法是类方法。
class Animal(object):
count = 0
@classmethod
def modify_count(cls, value):
print(cls) # <class '__main__.Animal'>
print(type(cls)) # <class 'type'>
cls.count = value # cls就是Animal类
a = Animal()
Animal.modify_count(5)
注意:在类方法中,只能访问类变量和类方法,不能访问实例成员。
1.2.7 静态方法
静态方法不与实例绑定,也不与类绑定,就是把类用于它的的命名空间。
定义与访问:
class 类:
@staticmethod
def 静态方法名(参数):
pass
访问:
类名.静态方法名(参数...)
@staticmethod
是装饰器,约束这个方法是静态方法。
class Animal(object):
@staticmethod
def add(a, b):
print("动物计算器:{}+{}={}".format(a, b, a + b))
Animal.add(3,5) # 动物计算器:3+5=8
强调:
在一个类中定义静态方法只是为了提供一个基于类名的命名空间。
1.3 类的访问权限
类内的属性可以让外部引用称为公有(public)属性,而方法称为公有方法。
缺点:外部可以随意修改内部的属性值,数据不安全。
python提供私有属性与方法,禁止外部直接访问。
1.3.1 私有变量
在名称前加两个下划线,表示这是一个私有属性:
class Animal(object):
def __init__(self):
self.__private_var = 5
a =Animal()
print(a.__private_var)
# 读取出错:AttributeError: 'Animal' object has no attribute '__private_var'
python没有严格意义的私有方法,实际上python把名字换了:
换成了: _类名__变量名
print(a._Animal__private_var) # 5
1.3.2 私有方法
同私有变量,就是名前加两个下划线即可。
1.4 定义属性
封装通常是对成员变量进行封装,严格来说应该定义私有变量,之后用getter,setter访问器访问。
把读写变成了函数。
使用getter,setter方法:
class Animal(object):
def __init__(self):
self.__age = 0 # 定义私有变量
# 用getter,setter函数访问这个私有变量
# getter
def get_age(self):
return self.__age
# setter
def set_age(self, age):
# 可添加一些约束
self.__age = age
a = Animal()
a.set_age(5)
print(a.get_age())
在python中提供了专门的定义getter,setter的装饰器(property)
- getter -> @property
- setter -> @属性名.setter
在定义时,要先定义getter, 再定义setter
class Animal(object):
def __init__(self):
self.__age = 0
@property
def age(self):
return self.__age
@age.setter
def age(self, age):
if age > 10:
print("赋值失败,年龄不能大于10!")
return
self.__age = age
a = Animal()
print(a.age) # 访问和变量的语法一样,但内部是一个函数。
a.age = 15
print(a.age)
1.5 继承
继承是面向象语言的基本特性,多态性的前提就是继承性。
1.5.1 继承
可以继承父类的公有成员变量和方法,不能继承私有的。
class 类名(父类):
def __init__(self,参数...):
super().__init__(参数...)
...
super()
表示父类的实例对象引用
super().__init__(参数)
这是调用父类的构造函数,用于初始化父类。注意这里没有self,因为super()就是父类的self了。
class Animal(object):
def __init__(self, name, age):
# 父类中定义了姓名和年龄变量
self.name = name
self.age = age
class Dog(Animal):
def __init__(self, name, age, home):
super().__init__(name, age)
# 子类中并没有定义姓名和年龄,但子类继承了父类中的这两个变量
self.home = home
dog = Dog('小白', 3, '张三')
print("{}{}岁了,是{}家的小狗".format(dog.name, dog.age, dog.home))
# 小白3岁了,是张三家的小狗
可以看出dog.name是访问继承下来的变量。
1.5.2 重写父类方法
父类的方法不能满足子类的需求,可以重写父类的方法。
class 父类:
def 方法(..):
pass
class 子类(父类)
def 方法(..): # 与父类的方法同名,子类对象调用时调用自己的,而不是继承的。
pass
class Animal(object):
def __init__(self):
pass
def eat(self):
print("Animal在吃东西")
class Dog(Animal):
def __init__(self):
super().__init__()
def eat(self):
#super().eat() 如果需要父类eat方法的功能,可以用super()调用
print("Dog在吃东西")
dog = Dog()
dog.eat() # Dog在吃东西
1.5.2 多继承
一个子类有多个父类:
- 多继承会发生冲突,python访问一个成员,先在自己的类中查找,如果没有再从第一个父类中查找,如果没有再找第二个父类……,如果没有再从父类的父类中查找……
class 子类(父类1,父类2,父类3...):
pass
class F():
def run(self):
print("F.run")
class F1(F):
def talk(self):
print("F1.talk")
class F2(F):
def talk(self):
print("F2.talk")
def music(self):
print("F2.music")
class FF(F1, F2):
pass
ff = FF()
ff.music() # F2.music
ff.talk() # F1.talk 虽然继承了两个talk但查找时按顺序先从父类1中查找。
ff.run() # F.run
1.6 多态
多态有两个条件:
- 继承性(一定发生子类与父类之间)
- 重写父类方法
多态在python中意义不大(因为python是动态语言)。在java等静态语言中:
- 编译期的变量声明是父类数据类型(因为具体实例化时不知道实例化的是哪种子类型,所以只能声明为父类类型,更具有通用性)。
- 在运行期由于变量所引用的实例是子类的实例(父类数据类型的变量引用子类实例),实际调用的是子类中的方法。
class Shape:
def draw(self):
pass
class Rect(Shape): # 继承
def draw(self): #重写父类方法
print("Rect.draw")
a: Shape = Rect() # 标记为父类变量
print(type(a)) # <class '__main__.Rect'>
a.draw() # Rect.draw
以java为例进行理解:
package test;
class Shape {
public void draw() {
System.out.println("Shape.draw");
}
}
class Rect extends Shape {
public void draw() {
System.out.println("Rect.draw");
}
}
class Circle extends Shape {
public void draw() {
System.out.println("Circle.draw");
}
}
public class Main {
public static void draw(Shape pen) {
//pen是一个基类的对象,但分配的实际对象可能是不同的形状
//这就产生了多态
pen.draw();
}
public static void main(String[] args) {
draw( new Rect()); //Rect.draw
draw( new Circle()); //Circle.draw
}
}
1.7 类型检查
python支持多态,可以通过isinstance(obj, type)
来检查对象是不是某个类或父类的实例。
class Shape:
def draw(self):
pass
class Rect(Shape):
def draw(self):
print("Rect.draw")
a = Rect()
print(isinstance(a,Shape)) # True
1.8 鸭子类型
动态语言的多态意义不大,可以使用“鸭子类型”替代多态性设计。
“鸭子类型”就是多个类具有相同的接口(没有继承),就可以像多态一样应用。
# 虽然没有继承,但仍然可以实现类多态性
class Animal:
def run(self):
print("Animal.run")
class Dog:
def run(self):
print("Dog.run")
class Car:
def run(self):
print("Car.run")
def run(p):
p.run()
run(Animal())
run(Dog())
run(Car())
'''
Animal.run
Dog.run
Car.run
'''
1.9 根类 object
python中所有类都继承自object类。
其中有两个方法经常用:
__str__()
__eq__(other)
1.9.1 __str__()
方法
打印对象时,对象调用这个方法生成字符串的表示形式(对象的类名,内存地址等信息)
可以重写这个方法实现自己需要的信息。
class Person:
def __init__(self, name, age):
self.__name = name
self.__age = age
@property
def name(self):
return self.__name
@property
def age(self):
return self.__age
@age.setter
def age(self, age):
if age < 0:
raise Exception("年龄非法")
self.__age = age
def __str__(self):
return "Person [ name={}, age={} ]".format(self.name, self.age)
print(Person("张三", 20)) # Person [ name=张三, age=20 ]
1.9.2 __eq__()
方法
当用 ==比较两个对象时,本质上是用对像里面的__eq__()
方法比较的。
可以重写比较方法。
class Person:
def __init__(self, name, age):
self.__name = name
self.__age = age
@property
def name(self):
return self.__name
@property
def age(self):
return self.__age
@age.setter
def age(self, age):
if age < 0:
raise Exception("年龄非法")
self.__age = age
def __str__(self):
return "Person [ name={}, age={} ]".format(self.name, self.age)
def __eq__(self, other):
return self.name == other.name and self.age == other.age
a = Person("张三",10)
b = Person("张三",10)
print(id(a), id(b))
print(a == b)
虽然两个对象的地址不同(不是同一个对象),但他们比较(按姓名和年龄)却相同。
1.10 枚举类
枚举是有限的常量集合
1.10.1定义方法:
class 枚举类(enum.Enum):
常量...
继承enum.Enum就可以了。
import enum
class Color(enum.Enum):
BLACK = 1
WHITE = 2
GREEN = 3
color = Color.BLACK
print(color) # Color.BLACK
print(color.value) # 1
print(color.name) # BLACK
其中:value是枚举值, name是枚举名
1.10. 2 限制枚举类
枚举类常量值应该是数字,而且不能重复。
- 继承: enum.IntEnum
- 用@enum.unique修饰
import enum
@enum.unique
class Color(enum.IntEnum):
BLACK = 1
WHITE = 2
GREEN = 3
# 可以遍历
for i in Color:
print(i.name, i.value)
'''
BLACK 1
WHITE 2
GREEN 3
'''
标签:__,PYTHON,self,class,面向对象,Animal,age,def
From: https://www.cnblogs.com/three-sheep/p/16954875.html