什么是类,对象,实例,类## 标题变量(类属性),实例变量(实例属性)
面向对象编程(Object-oriented Programming,简称 OOP),是一种封装代码的方法。比如说,将乱七八糟的数据扔进列表中,这就是一种简单的封装,是数据层面的封装;把常用的代码块打包成一个函数,这也是一种封装,是语句层面的封装。面向对象编程,也是一种封装的思想,把描述特征的数据和代码块(函数)封装到一起。
面向对象中,常用术语包括类,对象,属性,方法:
类:可以理解是一个模板,通过它可以创建出无数个具体实例(又称对象)。 对象(实例):类并不能直接使用,通过类创建出的实例才能使用。 属性:类中的所有变量称为属性。 方法:类中的所有函数通常称为方法。不过,和函数所有不同的是,类方法至少要包含一个 self 参数。类方法无法单独使用,只能和类的对象一起使用。
类变量和类属性的分类
无论是类属性还是类方法,都无法像普通变量或者函数那样,在类的外部直接使用它们。我们可以将类看做一个独立的空间,则类属性其实就是在类体中定义的变量,类方法是在类体中定义的函数。
在类体中,根据变量定义的位置不同,以及定义的方式不同,类属性又可细分为以下 3 种类型:
- 类体中、所有函数之外:此范围定义的变量,称为类属性或类变量;
- 类体中,所有函数内部:以“self.变量名”的方式定义的变量,称为实例属性或实例变量;
- 类体中,所有函数内部:以“变量名=变量值”的方式定义的变量,称为局部变量。
和类属性一样,类方法也可以进行更细致的划分,具体可分为类方法、实例方法和静态方法。在实际编程中,几乎不会用到类方法和静态方法,因为我们完全可以使用函数代替它们实现想要的功能,但在一些特殊的场景中(例如工厂模式中),使用类方法和静态方法也是很不错的选择。
和类属性的分类不同: 类方法:采用 @classmethod 修饰的方法 静态方法:采用 @staticmethod> 修饰的方法 实例方法:不用任何修改的方法
构造方法:在创建类时,我们可以手动添加一个 init() 方法,该方法是一个特殊的类实例方法,称为构造方法(或构造函数)。构造方法用于创建对象时使用,每当创建一个类的实例对象时,Python 解释器都会自动调用它。
类调用实例方法
实例方法的调用方式其实有 2 种,既可以采用类对象调用,也可以直接通过类名调用。
class CLanguage :
name = "python语言"
add = "http://c.biancheng.net.python"
def __init__(self,name,add):
self.name = name
self.add = add
print(name,"网址为:",add)
def say(self, content):
print(content)
def count(self,money):
sale = 0.8*money
print("优惠后的价格为:",sale)
@classmethod
def info(cls):
print("正在调用类方法",cls)
@staticmethod
def info(name,add):
print(name,add)
clang = CLanguage("C语言中文网1","http://c.biancheng.net")
CLanguage.say(clang, "通过类名直接调用实例方法")
clang2 = CLanguage("C语言中文网2","http://c.biancheng.net")
clang2.say("通过类对象直接调用实例方法")
C语言中文网1 网址为: http://c.biancheng.net
通过类名直接调用实例方法
C语言中文网2 网址为: http://c.biancheng.net
通过类对象直接调用实例方法
类的封装(enclosure),继承和多态
- 封装
简单的理解封装(Encapsulation),即在设计类时,刻意地将一些属性和方法隐藏在类的内部,这样在使用此类时,将无法直接以“类对象.属性名”(或者“类对象.方法名(参数)”)的形式调用这些属性(或方法),而只能用未隐藏的类方法间接操作这些隐藏的属性和方法。 Python 类如何进行封装?
和其它面向对象的编程语言(如 C++、Java)不同,Python 类中的变量和函数,不是公有的(类似 public 属性),就是私有的(类似 private),这 2 种属性的区别如下: public:公有属性的类变量和类函数,在类的外部、类内部以及子类(后续讲继承特性时会做详细介绍)中,都可以正常访问; private:私有属性的类变量和类函数,只能在本类内部使用,类的外部以及子类都无法使用。
Python 并没有提供 public、private 这些修饰符。为了实现类的封装,Python 采取了下面的方法: 默认情况下,Python 类中的变量和方法都是公有(public)的,它们的名称前都没有下划线(_); 如果类中的变量和函数,其名称以双下划线“”开头,但是没有以双下划线“”结尾,则该变量(函数)为私有变量(私有函数),其属性等同于 private。
封装的具体细节参考http://c.biancheng.net/view/2287.html
- 继承和多态
在OOP(Object Oriented Programming)程序设计中,当我们定义一个class的时候,可以从某个现有的class 继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。 继承有什么好处?最大的好处是子类获得了父类的全部属性及功能。 使用 class subclass_name(baseclass_name) 来表示继承
class Person(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
def print_title(self):
if self.sex == "male":
print("man")
elif self.sex == "female":
print("woman")
class Child(Person):
pass
May = Child("May","female")
Peter = Person("Peter","male")
print(May.name,May.sex,Peter.name,Peter.sex)
May.print_title()
Peter.print_title()
May female Peter male
woman
man
isinstance() 及 issubclass() Python 与其他语言不同点在于,当我们定义一个 class 的时候,我们实际上就定义了一种数据类型。我们定义的数据类型和Python自带的数据类型,比如str、list、dict没什么两样。
Python 有两个判断继承的函数:isinstance() 用于检查实例类型;issubclass() 用于检查类继承。参见下方示例:
class Person(object):
pass
class Child(Person):
pass
May = Child()
Peter = Person()
print(isinstance(May,Child))
print(isinstance(May,Person))
print(isinstance(Peter,Child))
print(isinstance(Peter,Person))
print(issubclass(Child,Person))
在说明多态是什么之前,我们在 Child 类中重写 print_title() 方法:若为male,print boy;若为female,print girl
class Person(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
def print_title(self):
if self.sex == "male":
print("man")
elif self.sex == "female":
print("woman")
class Child(Person):
def print_title(self):
if self.sex == "male":
print("boy")
elif self.sex == "female":
print("girl")
May = Child("May","female")
Peter = Person("Peter","male")
print(May.name,May.sex,Peter.name,Peter.sex)
May.print_title()
Peter.print_title()
当子类和父类都存在相同的 print_title()方法时,子类的 print_title() 覆盖了父类的 print_title(),在代码运行时,会调用子类的 print_title()
这样,我们就获得了继承的另一个好处:多态。
多态的好处就是,当我们需要传入更多的子类,例如新增 Teenagers、Grownups 等时,我们只需要继承 Person 类型就可以了,而print_title()方法既可以直不重写(即使用Person的),也可以重写一个特有的。这就是多态的意思。调用方只管调用,不管细节,而当我们新增一种Person的子类时,只要确保新方法编写正确,而不用管原来的代码。这就是著名的**“开闭”原则**:
对扩展开放(Open for extension):允许子类重写方法函数
对修改封闭(Closed for modification):不重写,直接继承父类方法函数
子类重写构造函数 子类可以没有构造函数,表示同父类构造一致;子类也可重写构造函数;现在,我们需要在子类 Child 中新增两个属性变量:mother 和 father,我们可以构造如下(建议子类调用父类的构造方法,参见后续代码):
class Person(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
class Child(Person):
def __init__(self,name,sex,mother,father):
Person.__init__(self,name,sex)
self.mother = mother
self.father = father
May = Child("May","female","April","June")
print(May.name,May.sex,May.mother,May.father)
多重继承 多重继承的概念应该比较好理解,比如现在需要新建一个类 baby 继承 Child , 可继承父类及父类上层类的属性及方法,优先使用层类近的方法,代码参考如下:
class Person(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
def print_title(self):
if self.sex == "male":
print("man")
elif self.sex == "female":
print("woman")
class Child(Person):
pass
class Baby(Child):
pass
May = Baby("May","female")
print(May.name,May.sex)
May.print_title()
class Child(Person):
def print_title(self):
if self.sex == "male":
print("boy")
elif self.sex == "female":
print("girl")
class Baby(Child):
pass
May = Baby("May","female")
May.print_title()
迭代,迭代器(iterator),可迭代对象(iterable object),生成器(generator)
迭代:是访问集合元素的一种方式。如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。 迭代对象: 如果类中定义了__iter__方法,且返回了一个迭代器对象, 那么称这个创建的对象为可迭代对象
字符串,列表,元祖,字典,集合等等,都是可迭代对象。 (没有__next__) 可迭代对象,则可用for循环
for i in [1, 2, 3]:
print(i)
obj = {"a": 123, "b": 456}
for k in obj:
print(k)
创建可迭代对象
class foo(object):
def __iter__(self):
return 迭代器对象(生成器对象)
obj = foo()
class IT(object):
def __init__(self):
self.counter = 0
def __iter__(self):
return self
def __next__(self):
self.counter += 1
if self.counter == 3:
raise StopIteration()
class foo(object):
def __iter__(self):
return IT()
obj = foo()
for item in obj:
print(item)
迭代器:迭代器是一个可以记住遍历的位置的对象
- 类中定义了__iter__和__next__两个方法
- __iter__返回对象本身,即self
- __next__返回下一个数据,如果没有,就返回一个stopiteration 的异常。
- 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
- 迭代器有两个基本的方法(内置函数):iter() 和 next() ;内置函数 iter() 将可迭代对象转化为迭代器. 通过 next() 方法逐一读取下一个元素
创建一个迭代器
创建迭代器共有3种方法如下:
- 通过python内置函数iter()将可迭代对象转换为迭代器
- 自己创建一个迭代器, 满足(1)类中定义了__iter__和__next__两个方法(2)__next__返回下一个数据,如果没有,就返回一个stopiteration> 的异常 2个条件
- 通过生成器(generator)创建
list=[1, 2, 3, 4]
it = iter(list)
print (next(it))
print (next(it))
for x in it:
print (x, end=" ")
print (next(it))
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
if self.a <= 20:
x = self.a
self.a += 1
return x
else:
raise StopIteration
myclass = MyNumbers()
myiter = iter(myclass)
print(next(myiter))
print(next(myiter))
print(next(myiter))
for x in myiter:
print(x)
print(next(myiter))
StopIteration
1
2
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Process finished with exit code 1
生成器:使用了 yield 的 函数 被称为生成器(generator) 具体细节参考http://c.biancheng.net/view/2393.html
相比迭代器,生成器最明显的优势就是节省内存空间,即它不会一次性生成所有的数据,而是什么时候需要,什么时候生成。
创建一个生成器
- 定义一个以 yield 关键字标识返回值的函数;
- 调用刚刚创建的函数,即可创建一个生成器
要想使生成器函数得以执行,或者想使执行完 yield 语句立即暂停的程序得以继续执行,有以下 2 种方式:
- 通过生成器(上面程序中的 num)调用 next() 内置函数或者 next() 方法;
- 通过 for 循环遍历生成器。
def intNum():
print("开始执行")
for i in range(5):
yield i
print("继续执行")
num = intNum()
print(next(num))
print(num.__next__())
for i in num:
print(i)
开始执行
0
继续执行
1
继续执行
2
继续执行
3
继续执行
4
继续执行
Process finished with exit code 0
除此之外,还可以使用 list() 函数和 tuple() 函数,直接将生成器能生成的所有值存储成列表或者元组的形式
num = intNum()
print(list(num))
num = intNum()
print(tuple(num)
开始执行
继续执行
继续执行
继续执行
继续执行
继续执行
[0, 1, 2, 3, 4]
开始执行
继续执行
继续执行
继续执行
继续执行
继续执行
(0, 1, 2, 3, 4)
Process finished with exit code 0
参考: 2. C语言中文网 http://c.biancheng.net/ 3. https://www.cnblogs.com/feeland/p/4419121.html 4. https://www.liaoxuefeng.com/wiki/1016959663602400/1017316949097888
|