最近很多小伙伴都在问pyhon中类的复杂继承--------类的基础和八这两个问题,那么本篇文章就来给大家详细解答一下,同时本文还将给你拓展C++类的复合和继承关系(C++继承类和封闭类的关系)、JA
最近很多小伙伴都在问pyhon中类的复杂继承--------类的基础和八这两个问题,那么本篇文章就来给大家详细解答一下,同时本文还将给你拓展C++类的复合和继承关系(C++继承类和封闭类的关系)、JAVA基础学习之路(八)[1]String类的基本特点、Python 基础(八)、Python 基础(八)进阶等相关知识,下面开始了哦!
本文目录一览:- pyhon中类的复杂继承--------类的基础(八)(python中类的继承)
- C++类的复合和继承关系(C++继承类和封闭类的关系)
- JAVA基础学习之路(八)[1]String类的基本特点
- Python 基础(八)
- Python 基础(八)进阶
pyhon中类的复杂继承--------类的基础(八)(python中类的继承)
def who(obj):
print(id(obj),type(obj))
class A(object):
def method(self):
who(self)
print("A.method\n")
class B(A):
def method(self):
who(self)
print("B.method\n")
class C(B):
def method(self):
who(self)
print("C.method\n")
''''''super(): same as super(__class__,<first argument>)
super(type):unbound super object
super(type,object):bound super object,requires isinstance(obj,type)
super(type,type2):bound super object,requires issubclass(type2,type)''''''
class D(C):
def __init__(self):
super().method()
super(__class__,self).method()
super(C,self).method()# call C的 parent''s method,print B.method
super(B,self).method()# call B的 parent''s method,print A.method
super(B,C()).method()#calling B的parent''s method,print A.method
#with instance of C
d = D()
#c = C()
print("\n Instance of D:\n")
who(d)
super(D,d).method()#从外部使用super方法,调用D的父类.method,通过产生d的实例
C++类的复合和继承关系(C++继承类和封闭类的关系)
在 C++ 中,类和类之间有两种基本关系:复合关系和继承关系。复合关系也称为“has a”关系或“有”的关系,表现为封闭类,即一个类以另一个类的对象作为成员变量。如上节中 CStudent 类的例子,每个 CStudent 对象都“有”一个 string 类的成员变量 name,代表姓名。
继承关系也称为“is a”关系或“是”的关系,即派生类对象也是一个基类对象。如在上节的程序中,CUndergraduateStudent 类(代表本科生)继承了 CStudent 类(代表学生)。因为本科生也是学生,因此可以说,每一个 CUndergraduateStudent 类的对象也是一个 CStudent 类的对象。
在设计两个有关系的类时要注意,并非两个类有共同点,就可以让它们成为继承关系。让类 B 继承类 A,必须满足“类 B 所代表的事物也是类 A 所代表的事物”这个命题从逻辑上是成立的。例如,写一个平面上的点类 CPoint::
class CPoint{ double x,y; //点的坐标 };又要写一个圆类 CCircle。CCircle 类有圆心,圆心也是平面上的一点,因而 CCircle 类和 CPoint 类似乎有相同的成员变量。如果因此就让 CCircle 类从 CPoint 类派生而来,即采用如下写法:
class CCircle: public CPoint{ double radius; //半径 };是不正确的。因为,“圆也是点”这个命题是不成立的。这个错误不但初学者常犯,甚至很多知名教材也以此作为继承的例子。正确的做法是使用“has a”关系,即在 CCircle 类中引入 CPoint 成员变量,代表圆心:
class CCircle { CPoint center; //圆心 double radius; //半径 }这样,从逻辑上来说,每一个“圆”对象都包含(有)一个“点”对象,这个“点”对象就是圆心——这非常合理。
如果写了一个 CMan 类代表男人,后来发现又需要一个 CWoman 类代表女人,仅仅因为 CWoman 类和 CMan 类有共同之处,就让 CWoman 类从 CMan 类派生而来,同样也是不合理的。因为“一个女人也是一个男人”从逻辑上不成立。
但是让 CWoman 类包含 CMan 类成员对象就更不合适了。
此时正确的做法应该是概括男人和女人的共同特点,编写一个 CHuman 类,代表“人”,然后 CMan 类和 CWoman 类都从 CHuman 类派生。
有时,复合关系也不一定都是通过封闭类实现的,尤其当类 A 中有类 B,类 B 中又有类 A 的情况。
假设要编写一个小区养狗管理程序,该程序需要一个“主人”类,还需要一个“狗”类。狗是有主人的,主人也有狗。假定狗只有一个主人,但一个主人可以有最多 10 条狗。该如何处理“主人”类和“狗”类的关系呢?下面是一种直观的写法:
class CDog; class CMaster //主人 { CDog dogs[10]; int dogNum; //狗的数量 }; class CDog { CMaster m; };这种写法是无法通过编译的。因为尽管提前对 CDog 类进行了声明,但编译到第 4 行时,编译器还是不知道 CDog 类的对象是什么样的,所以无法编译定义 dog 对象的语句。而且这种“人中有狗,狗中有人”的做法导致了循环定义。
避免循环定义的方法是在一个类中使用另一个类的指针,而不是对象作为成员变量。例如下面的写法:
class CDog; class CMaster { CDog* dogs[10]; int dogNum; //狗的数量 }; class CDog { CMaster m; };上面这种写法在第 4 行定义了一个 CDog 类的指针数组作为 CMaster 类的成员对象。指针就是地址,大小固定为 4 个字节,所以编译器编译到此时不需要知道 CDog 类是什么样子。
这种写法的思想是:当一个 CMaster 对象养了一条狗时,就用 new 运算符动态分配一个 CDog 类的对象,然后在 dogs 数组中找一个元素,让它指向动态分配的 CDog 对象。
这种写法还是不够好。问题出在 CDog 对象中包含了 CMaster 对象。在多条狗的主人相同的情况下,多个 CDog 对象中的 CMaster 对象都代表同一个主人,这造成了没有必要的冗余:一个主人用一个 CMaster 对象表示足矣,没有必要对应于多个 CMaster 对象。
而且,在一对多这种情况下,当主人的个人信息发生变化时,就需要将与其对应的、位于多个 CDog 对象中的 CMaster 成员变量 m 都找出来修改,这毫无必要,而且非常麻烦。
正确的写法应该是为“狗”类设一个“主人”类的指针成员变量,为“主人”类设一个“狗”类的对象数组。如下所示:
class CMaster; classCDog { CMaster* pm; }; class CMaster { CDog dogs[10]; int dogNum; };这样,主人相同的多个 CDog 对象,其 pm 指针都指向同一个 CMaster 对象。
实际上,每个主人未必都养 10 条狗,因此出于节省空间的目的,在 CMaster 类中设置 CDog 类对象的指针数组,而不是对象数组,也是一种好的写法。如下所示:
class CMaster { CDog* dogs[10]; int dogNum; };有的教材将类 A 的成员变量是类 B 的指针这种情况称为“类 A 知道类 B”,两个类之间是“知道”关系。
JAVA基础学习之路(八)[1]String类的基本特点
String类的两种定义方式:
-
直接赋值
-
通过构造方法赋值
//直接赋值
public class test2 {
public static void main(String args[]) {
String str = "hello";
System.out.println(str);
}
}
//通过构造方法
public class test2 {
public static void main(String args[]) {
String str = new String("hello");
System.out.println(str);
}
}
但是,两种方法却有着极大的区别
主要体现在内存上
一个简单的例子:
public class test1 {
public static void main(String args[]) {
int a = 10;
int b = 10;
String str_1 = "hello";
String str_2 = "hello";
String str_3 = new String("hello");
System.out.println(a==b);
System.out.println(str_1==str_2);
System.out.println(str_1==str_3);
System.out.println(str_2==str_3);
}
} //输出为true true false false
按道理来说,应该是四个true,因为他们的值都是相等的啊.......但是.....
上图解释了两个通过直接赋相同值产生的字符串使用“”==“”为什么返回true。
为什么会全部指向一块堆内存而不是分别指向自己的堆内存呢???
共享设计模式:
在JVM底层有一个对象池,里面有包括String在内的许多对象,当代码之中使用了直接赋值的方式定义了一个String类对象时,会将此字符串使用的匿名对象加入对象池。之后若有采用直接赋值的方式定义字符串,并且赋了相同的值,那么不会开辟新的堆内存空间,而是使用已有的对象(堆内存)进行继续使用。
上图解释了为什么直接赋值和构造方法赋相同的值,然而返回值却为false。因为使用构造方法定义字符串,使用了new关键字,意味着产生一块新的堆内存。那么两种方法各自开辟了一块堆内存,“==”符号对于字符串来说,比较的是地址值。两块堆内存的地址不同,所以返回值为false。
若要比较字符串的内容,而不是地址,应该使用str_a.equals(str_b)
字符串常量就是String类的匿名对象,所谓的直接赋值实际上就是为匿名对象加了一个名字。但是String类的匿名对象是由系统自动生成的,不用用户自己创建
public class test1 {
public static void main(String args[]) {
String str = "hello";
System.out.println("hello".equals(str));
}
}
//返回值为true
tips:
防止空指向异常(使用了未实例化的对象):将字符串写在输入的前面
public class test1 {
public static void main(String args[]) {
String input = null;
System.out.println(input.equals("hello"));
}
}
//Exception in thread "main" java.lang.NullPointerException
at test.test1.main(test1.java:6) 空指向异常
public class test1 {
public static void main(String args[]) {
String input = null;
System.out.println("hello".equals(input));
}
}
//false
两种定义方法的优劣:
直接赋值节约空间
然而构造方法却会产生大量垃圾,如下图:
代码从右向左看,产生的第一块堆内存会成为垃圾
构造方法实例化的方法除了浪费内存外,其定义的对象不会保存在对象池之中,若要保存,需要手动入池(使用inner()方法)
public class test1 {
public static void main(String args[]) {
String str_1 = new String("hello").intern();
String str_2 = "hello";
System.out.println(str_1==str_2);
}
}
//true
返回值为true,证明入池成功。
Python 基础(八)
1 模块
1.1 模块
模块是一个 Python
文件,其中包含对象定义与语句等,多个模块再加上__init__.py
就变成了一个包。
1.2 模块存在形式
模块可以分成两种:
- 非内置模块
- 内置模块
1.2.1 非内置模块
非内置模块以.py
文件的形式存在,放在对应的包下。
1.2.2 内置模块
内置模块就是内置到解释器中的模块,为了提高性能内置模块采用 C
语言而不是 Python
编写。
1.3 模块使用
先导入再调用里面的函数,可以一次导入整个模块,或者选择性导入其中的函数。
1.3.1 导入整个模块
import math
import cmath as cm
使用 as
设置模块别名。
1.3.2 导入模块中的某个函数
from math import sin
from cmath import sqrt as cs
使用 as
设置函数别名。
1.4 查看内置模块
import sys
sys.builtin_module_names
2 包
2.1 包
包是含有一个特殊的__init__.py
文件的文件夹,通常还含有其他.py
文件,表示不同的模块。
2.2 包管理
包的下载、安装、更新、删除可通过包管理器进行操作,常用的包管理器为 pip
与 Conda
。pip
设计目的是仅管理 Python
语言的包,对应的包服务器为 PyPI,Conda
设计目的是管理多种语言的包,对应的包服务器为 Conda。
2.3 包管理命令
2.3.1 列出已安装的包
pip list
conda list
2.3.2 更新包
pip install --upgrade package_name
conda upgrade package_name
2.3.3 删除包
pip uninstall package_name
conda uninstall package_name
2.3.4 安装包
pip install package_name
conda install package_name
Python 基础(八)进阶
一、数据类型的底层实现
-
列表
1)复制
为什么修改浅拷贝后的列表,原列表数据也被修改了?
2)列表的底层实现
列表在内存中是如何存储的?列表只是存储了元素的地址,元素是分散地存储在内存中
直接赋值:取个别名
浅拷贝:将地址复制了一份新的,还是指向原来的元素浅拷贝的操作:
总结:对于可变类型,如:列表、字典,改变浅拷贝后的值,指向的地址不变,原值会受影响,对于不可变类型,如:元组、数字、字符串,改变浅拷贝后的值,指向的地址更改,原值不会受影响。引入深拷贝:将所有层级的相关元素全部复制,原本数据不会受影响。
-
字典
1)快速查找
对比:列表
字典
2)字典的底层实现
字典在内存中是如何存储的?存在稀疏数组(动态散列表)中,再通过哈希函数计算的key的散列值找到value,空间换时间 -
字符串
存储:紧凑数组,即不用指针,在内存中连续存放,节省空间 -
可变与否
1)不可变类型:数字、字符串、元组,其+=操作实际上是创建了一个新的对象
元组并不总是不可变
2)可变类型:内容可变 -
优化列表操作
1)删除列表内的特定元素
法1:存在运算删除
缺点:每次都要从头到尾对列表进行遍历和查找,效率低
法2:使用负向索引
2)多维列表的创建
错误:将[0]*10复制5遍
正确:用解析语法——独立创建5个[0]*10
二、简洁的语法
- 解析语法
1)结构(以列表为例):[expr for value in iterable if condition]
2)例子:求20内奇数的平方
支持嵌套
- 条件表达式
expr1 if condition else expr2
例:将变量n的表达式赋给x
三、三大神器
-
生成器
1)定义:惰性计算,无需一次性存储海量数据,一边执行一边计算,只计算每次所需值,一直执行next(),直到无值可取。
2)生成方式
生成器表达式:(expr for value in range())
生成器函数——yield
每次调用next()时执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行
-
迭代器
1)可迭代对象:可由for循环对其进行遍历的对象
列表、元组、字符串、字典、集合、文件
可用isinstance(对象, Iterable)来判断
生成器
2)迭代器定义:可以被next()函数调用并不断返回下一个值,直至没有数据可取。
可用isinstance(对象, Iterator)来判断
生成器是迭代器,列表、元组、字符串、字典、集合、文件不是,但可通过iter(Iterable)创造迭代器
python库itertools中的函数是迭代器,例:zip()、enumerate()
文件是迭代器3)迭代器运算:
for value in iterator: expr
4)迭代器会被耗尽
5)range() 不是迭代器,是懒序列:不包含任何内存中的内容,当调用时立刻计算来回答问题 -
装饰器
用来增加一些已开发程序的某些功能,同时不修改函数的源码及调用方式
1)函数对象:函数是Python中的第一类对象,可将函数赋值给变量,对该变量进行调用,可实现函数的功能,可将函数视为参数进行传递2)高阶函数:接收函数作为参数或返回一个函数
3)嵌套函数:函数内部再定义函数4)闭包:闭包=函数+引用环境,若一个函数定义在另一个函数的作用域内,并引用了外层函数的变量,则该函数为闭包。
一旦在内层函数重新定义了相同名字的变量,则变量成为局部变量。
可用nonlocal来允许内嵌函数修改闭包变量
5)一个简单的装饰器
嵌套函数实现
语法糖:用@修饰函数放在原函数上
若被修饰函数有参数,则内层函数加上形参
若被修饰函数有返回值,则内层函数创建一个参数保存
若装饰器本身要传递一些额外参数,通过参数,对函数实行不同的修饰
6)使装饰后原函数被覆盖的属性重新回来:再用一个语法糖在内层函数上
关于pyhon中类的复杂继承--------类的基础和八的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于C++类的复合和继承关系(C++继承类和封闭类的关系)、JAVA基础学习之路(八)[1]String类的基本特点、Python 基础(八)、Python 基础(八)进阶等相关知识的信息别忘了在本站进行查找喔。
本文标签: