精华内容
下载资源
问答
  • Jupyter Notebooks嵌入Excel并使用Python替代VBA

    万次阅读 多人点赞 2020-12-24 09:38:36
    弄清楚如何使用Excel对象模型进行操作的一种好方法是记录VBA,然后将该转换为Python! PyXLL文档页面Python作为VBA的替代品提供了一些有关如何做到这一点的技巧。 总结 Python是VBA的强大替代品。 使用PyXLL,你...

    以前,Excel和Python Jupyter Notebook之间我们只能选择一个。 但是现在随着PyXLL-Jupyter软件包的推出,可以将两者一起使用。

    在本文中,我将向你展示如何设置在Excel中运行的Jupyter Notebook。 在这两者之间共享数据,甚至可以从Excel工作簿调用Jupyter笔记本中编写的Python函数!

    开始

    首先,要在Excel中运行Python代码,你需要使用PyXLL包。 PyXLL使我们可以将Python集成到Excel中,并使用Python代替VBA。 要安装PyXLL Excel加载项“ pip install pyxll”,然后使用PyXLL命令行工具安装Excel的加载项:

    >> pip install pyxll
    >> pyxll install
    

    安装完PyXLL Excel插件,下一步就是安装PyXLL -jupyter软件包。该软件包提供了PyXLL和Jupyter之间的链接,因此我们可以在Excel内使用Jupyter笔记本。

    使用pip安装pyxll-jupyter包:

    >> pip install pyxll-jupyter
    

    一旦安装了PyXLL Excel加载项和PyXLL-Jupyter软件包后,启动Excel将在PyXLL选项卡中看到一个新的“ Jupyter”按钮。

    单击此按钮可在Excel工作簿的侧面板中打开Jupyter笔记本。 该面板是Excel界面的一部分,可以通过拖放操作取消停靠或停靠在其他位置。

    在Jupyter面板中,你可以选择一个现有的笔记本或创建一个新的笔记本。 要创建一个新的笔记本,请选择“新建”按钮,然后选择“ Python 3”。

    如何使用

    现在,你已经在Excel中运行了完整的Jupyter笔记本! 但是,这有什么好处呢? 这比在Excel外部运行笔记本更好?

    好了,现在你可以使用Excel处理数据,并使用Python处理相同的数据。 将Excel用作用于组织和可视化数据的交互式操作,无缝切换到Python以使用更复杂的功能。

    将Jupyter笔记本用作草稿板,以试用Python代码。在Jupyter笔记本上完全用Python编写Excel函数,并进行实时测试。开发完一个有用的可重用函数后,将其添加到PyXLL Python项目中。这样你每次使用Excel时都可以使用相同的函数。

    在本文的其余部分,我将向你展示如何:

    • 使用Jupyter笔记本在Excel和Python之间共享数据
    • 在笔记本上写Excel工作表函数(udf)
    • 脚本Excel与Python代替VBA

    从Excel获取数据到Python

    因为PyXLL在与Excel相同的进程中运行Python,所以用Python访问Excel数据以及在Python和Excel之间快速调用。

    为了使事情尽可能简单,pyxll-jupyter包附带了一些IPython“魔法”函数,可以在你的Jupyter笔记本中使用。

    % xl_get

    excel sheet 与 Pandas DataFrames 同步

    使用魔术函数“%xl_get”来获取Python中当前的Excel选择。 在Excel中创建数据表, 选择左上角(或整个范围),然后在Jupyter笔记本中输入“%xl_get”,瞧! Excel表现在是pandas DataFrame。

    %xl_get魔术函数有几个选项:

     -c或--cell。 传递单元格的地址以获取值,例如%xl_get --cell A1:D5。
     -t或--type。 指定获取值时要使用的数据类型,例如%xl_get --type numpy_array。
     -x或--no-auto-resize。 仅获取选定范围或给定范围的数据。 不要扩展到包括周围的数据范围。
    

    PyXLL还有其他与Excel交互以将数据读入Python的方式。 “%xl_get”魔术功能只是使事情变得更简单! 当Jupyter笔记本在Excel中运行时,所有其他方法(例如,使用XLCell类,Excel的COM API甚至xlwings)仍然可用。

    提示:可以为魔术函数的结果分配一个变量! 例如,尝试“ df =%xl_get”。

    将Python中的数据移回Excel

    从Python到Excel的另一种传输方式也可以正常工作。 无论你是使用Python加载数据集并将其传输到Excel工作簿,还是通过Excel处理数据集并希望将结果返回Excel,从Python复制数据到Excel都很容易。

    %xl_set
    

    魔术函数“%xl_set”获取一个Python对象并将其写入Excel。在Excel中是否有想要的数据框“ df”?只需使用“%xl_set df”,它将被写入Excel中的当前选择。

    与%xl_get一样,%xl_set也具有一系列选项来控制其行为。你甚至可以使用PyXLL的单元格格式设置功能在将结果写入Excel的同时自动应用格式设置。

    -c或--cell。将值写入的单元格地址,例如%xl_set VALUE --cell A1。
    -t或--type。将值写入Excel时要使用的数据类型说明符,例如%xl_set VALUE --type dataframe <index = False>。
    -f或--formatter。 PyXLL单元格格式化程序对象,例如%xl_set VALUE --formatter DataFrameFormatter()。请参阅单元格格式。
    -x或--no-auto-resize。不要自动调整范围大小以适合数据。仅将值写入当前选择或指定范围。
    

    与%xl_get一样,%xl_set只是一个快捷方式,你可能已与PyXLL一起使用的所有其他写回Excel的方式仍然可以在Jupyter笔记本中使用。

    在Excel中使用Python图(matplotlib / plotly等)

    关于数据处理的一大优点是可用的功能强大的绘图程序包。 例如df.plot()

    PyXLL集成了所有主要的绘图库,因此你也可以在Excel中充分利用它们。 这包括matplotlib(由pandas使用),plotly,bokeh和altair。

    %xl_plot
    

    使用“%xl_plot”在Excel中绘制任何Python图表。 从一个受支持的绘图库中向其传递任何图形对象,或使用最后一个pyplot图形。 使用pandas plot的效果也很好,例如。 %xl_plot df.plot(kind=‘scatter’).

    %xl_plot魔术函数具有一些选项来控制其工作方式:

     -n或--name。 Excel中图片对象的名称。 如果使用已经存在的图片名称,则该图片将被替换。
     -c或--cell。 用作新图片位置的单元格地址。 如果图片已经存在,则无效。
     -w或--width。 Excel中图片的宽度(以磅为单位)。 如果更新现有图片,则无效。
     -h或--height。 Excel中图片的高度(以磅为单位)。 如果更新现有图片,则无效。
    

    %xl_plot是pyxll.plot函数的快捷方式。

    从Excel调用Python函数

    你可以直接从Excel工作簿中调用Python函数,而不是在Excel和Jupyter之间不断移动数据然后运行一些Python代码

    PyXLL的主要用例之一是用Python编写自定义Excel工作表函数(或“ UDF”)。 这用于在使用Python函数构建的Excel中构建模型,这些函数当然可以使用其他Python库(例如pandas和scipy)。

    你也可以在Jupyter笔记本中编写Excel工作表函数。 这是在不离开Excel即可使用Python IDE的情况下尝试想法的绝佳方法。

    自己试试吧。 编写一个简单的函数,然后将“ pyxll.xl_func”修饰符添加到你的函数中:

    from pyxll import xl_func
    
    @xl_func
    def test_func(a, b, c):
        # This function can be called from Excel!
        return (a * b) + c
    

    输入代码并在Jupyter中运行单元格后,即可立即从Excel工作簿中调用Python函数。

    不只是简单的功能。 你可以将整个数据范围作为pandas DataFrames传递给函数,并返回任何Python类型,包括numpy数组和DataFrames! 你可以通过给@xl_func装饰器一个参数字符串来告诉PyXLL期望什么类型。

    例如,尝试以下方法:

    from pyxll import xl_func
    
    # The "signature" tells PyXLL how to convert the arguments
    # and returned value.
    @xl_func("dataframe df: dataframe<index=True>", auto_resize=True)
    def df_describe(df):
        # 'df' is a pandas DataFrame built from the range passed
        # to this function.
        desc = df.describe()
        
        # 'desc' is a new DataFrame, which PyXLL will convert to
        # a range of values when returning to Excel.
        return desc
    

    现在,你可以编写复杂的Python函数来进行数据转换和分析,Excel中如何调用或排序这些函数。 更改输入会导致调用函数,并且计算出的输出会实时更新,这与你期望的一样!

    在Excel中使用Python而不是VBA的脚本

    你是否知道在VBA中可以执行的所有操作也可以在Python中完成?编写VBA时将使用Excel对象模型,但是Python也提供相同的API。

    在Excel中运行的Jupyter笔记本中,可以使用整个Excel对象模型,因此你可以使用与Excel VBA编辑器中完全相同的方式编写Excel脚本。

    由于PyXLL在Excel进程内运行Python,因此从Python调用Excel不会对性能造成任何影响。也可以从外部Python进程调用Excel,但这通常要慢得多。在Excel中运行Jupyter笔记本也使一切变得更加便捷!

    使用PyXLL的xl_app函数获取“ Excel.Application”对象,该对象等效于VBA中的Application对象。尝试进行诸如获取当前选择和更改单元格内部颜色之类的操作。弄清楚如何使用Excel对象模型进行操作的一种好方法是记录VBA宏,然后将该宏转换为Python! PyXLL文档页面Python作为VBA的替代品提供了一些有关如何做到这一点的技巧。

    总结

    Python是VBA的强大替代品。 使用PyXLL,你可以完全用Python编写功能齐全的Excel加载项。 Excel是一种出色的交互式计算工具。 添加Python和Jupyter将Excel提升到一个全新的水平。

    使用Jupyter笔记本编写的代码可以轻松地重构为独立的Python包,以创建Excel工具包来为直观的工作簿和仪表板提供动力。 任何Excel用户都将能够利用使用PyXLL编写的Python工具,而无需任何Python知识。

    最后 PyXLL的官网地址:https://www.pyxll.com/blog/python-jupyter-notebooks-in-excel/

    作者:Tony Roberts

    deephub翻译组

    展开全文
  • Ext2索引节点对象创建

    千次阅读 2011-01-31 21:42:00
    1.2.3 Ext2索引节点对象创建 现在还是从一个普通文件的角度来分析上面的过程,比如说,当我门在根目录下调用fd = open("file", O_CREAT)打开(创建)一个文件时,会启动do_sys_open系统调用,并根据路径...

    1.2.3 Ext2索引节点对象的创建

    现在还是从一个普通文件的角度来分析上面的过程,比如说,当我门在根目录下调用fd = open("file", O_CREAT)打开(创建)一个文件时,会启动do_sys_open系统调用,并根据路径“file”去触发do_filp_open函数返回一个file结构。do_filp_open主要调用的两个函数(详细的过程请参考博客“VFS系统调用的实现”

    http://blog.csdn.net/yunsongice/archive/2010/06/22/5685130.aspx):

     

    1open_namei():填充目标文件所在目录(也就是根目录)的dentry结构和所在文件系统的vfsmount结构,并将信息保存在nameidata结构中。在dentry结构中dentry->d_inode就指向目标文件的索引节点。

     

    2dentry_open():建立目标文件的一个“上下文”,即file数据结构,并让它与当前进程的task_strrct结构挂上钩。同时,在这个函数中,调用了具体文件系统的打开函数,即f_op->open(),也就是前面看到的generic_file_open,,该函数返回指向新建立的file结构的指针。

     

    注意,如果在访问模式标志中设置了O_CREAT,比如我们这里,则以LOOKUP_PARENTLOOKUP_OPENLOOKUP_CREATE标志的设置开始查找操作。一旦path_lookup()函数成功返回,则检查请求的文件是否已存在。如果不存在,则open_namei会调用父索引节点的create方法分配一个新的磁盘索引节点。这里父索引节点是一个目录的,所以其i_op方法不是上面提到的ext2_file_inode_operations,而是ext2_dir_inode_operations,来自fs/ext2/namei.c

     

    const struct inode_operations ext2_dir_inode_operations = {

           .create            = ext2_create,

           .lookup          = ext2_lookup,

           .link        = ext2_link,

           .unlink           = ext2_unlink,

           .symlink  = ext2_symlink,

           .mkdir            = ext2_mkdir,

           .rmdir            = ext2_rmdir,

           .mknod          = ext2_mknod,

           .rename          = ext2_rename,

    #ifdef CONFIG_EXT2_FS_XATTR

           .setxattr   = generic_setxattr,

           .getxattr  = generic_getxattr,

           .listxattr  = ext2_listxattr,

           .removexattr   = generic_removexattr,

    #endif

           .setattr     = ext2_setattr,

           .check_acl      = ext2_check_acl,

    };

     

    ext2_create函数定义在同一个文件中,传给它的参数是根目录的inode,:

     

    static int ext2_create (struct inode * dir, struct dentry * dentry, int mode, struct nameidata *nd)

    {

           struct inode *inode;

     

           dquot_initialize(dir);

     

           inode = ext2_new_inode(dir, mode);

           if (IS_ERR(inode))

                  return PTR_ERR(inode);

     

           inode->i_op = &ext2_file_inode_operations;

           if (ext2_use_xip(inode->i_sb)) {

                  inode->i_mapping->a_ops = &ext2_aops_xip;

                  inode->i_fop = &ext2_xip_file_operations;

           } else if (test_opt(inode->i_sb, NOBH)) {

                  inode->i_mapping->a_ops = &ext2_nobh_aops;

                  inode->i_fop = &ext2_file_operations;

           } else {

                  inode->i_mapping->a_ops = &ext2_aops;

                  inode->i_fop = &ext2_file_operations;

           }

           mark_inode_dirty(inode);

           return ext2_add_nondir(dentry, inode);

    }

     

    这里面最重要的是ext2_new_inode()函数,用于在父目录dir下创建Ext2磁盘的索引节点,返回相应的索引节点对象的地址(或失败时为NULL)。该函数谨慎地选择存放该新索引节点的块组;它将无联系的目录散放在不同的组,而且同时把文件存放在父目录的同一组。为了平衡普通文件数与块组中的目录数,Ext2为每一个块组引入“债(debt) ”参数。

     

    ext2_new_inode函数有两个参数:dir,所创建索引节点父目录对应的索引节点对象的地址,新创建的索引节点必须插入到这个目录中,成为其中的一个目录项;mode,要创建的索引节点的类型。后一个参数还包含一个MS_SYNCHRONOUS标志,该标志请求当前进程一直挂起,直到索引节点被分配成功或失败。该函数代码时分复杂,详细的分析内容请参考博客“Ext2索引节点分配” http://blog.csdn.net/yunsongice/archive/2010/08/18/5822472.aspx,这里仅仅概要地讲解一下分配步骤。

     

    ext2_new_inode函数首先调用new_inode()通过sb->s_op->alloc_inode函数分配一个新的VFS索引节点对象,并把它的i_sb字段初始化为存放在dir->i_sb中的超级块地址,然后把它追加到正在用的索引节点链表与超级块链表中。

     

    前面看到,superblocks_op->alloc_inode函数地址为ext2_alloc_inode,该函数来自fs/ext2/super.c

     

    static struct inode *ext2_alloc_inode(struct super_block *sb)

    {

           struct ext2_inode_info *ei;

           ei = (struct ext2_inode_info *)kmem_cache_alloc(ext2_inode_cachep, GFP_KERNEL);

           if (!ei)

                  return NULL;

           ei->i_block_alloc_info = NULL;

           ei->vfs_inode.i_version = 1;

           return &ei->vfs_inode;

    }

    很简单,通过ext2_inode_cachepslab分配器分配一个磁盘索引节点描述符ext2_inode_info而作为VFSinode结构作为嵌入其内部的vfs_inode字段被返回。注意,VFSinode对象是嵌入在ext2_inode_info描述符中的,当执行了ext2_alloc_inode函数以后,new_inode()函数内部就会有一个未初始化的inode结构。随后,new_inode()函数将这个inode分别插入到以inode_in_usesb->s_inodes为首的循环链表中。

     

    因为inode分别嵌入到ext2_inode_info结构中的,所以内核提供EXT2_I宏来获得已分配inodeext2_inode_info结构:

     

    static inline struct ext2_inode_info *EXT2_I(struct inode *inode)

    {

           return container_of(inode, struct ext2_inode_info, vfs_inode);

    }

    #define container_of(ptr, type, member) ({                /

            const typeof( ((type *)0)->member ) *__mptr = (ptr); /

            (type *)( (char *)__mptr - offsetof(type,member) );})

    struct ext2_inode_info *ei = EXT2_I(inode);

     

    那么super_blockext2_sb_info的关系是不是这样的呢?上一节我们看到,磁盘中的ext2_super_block结构总是被页高速缓存到内存中,由ext2_sb_info结构的s_es字段指向,所以内核提供的EXT2_SB宏就能直接得到ext2_sb_info数据结构位于内存的地址。

     

    static inline struct ext2_sb_info *EXT2_SB(struct super_block *sb)

    {

           return sb->s_fs_info;

    }

    struct ext2_sb_info *sbi = EXT2_SB(sb);

    struct ext2_super_block *es = sbi->s_es;

     

    因此,ext2_new_inode函数内部,与分配的inode相关的磁盘索引节点映像ext2_inode_info、磁盘超级快映像ext2_sb_info和磁盘超级快对象ext2_super_block分别由内部变量eisbies指向。

     

    随后如果新的索引节点是一个目录,函数就调用find_group_orlov(sb, dir)为目录找到一个合适的块组(安装ext2时,如果带一个参数OLDALLOC,则会强制内核使用一种简单、老式的方式分配块组:find_group_dir(sb, dir))。

     

    如果新索引节点不是个目录,则调用find_group_other(sb, dir),在有空闲索引节点的块组中给它分配一个。该函数从包含父目录的块组开始往下找。具体逻辑如下:

     

    a.       从包含父目录dir的块组开始,执行快速的对数查找。这种算法要查找log(n)个块组,这里n是块组总数。该算法一直向前查找直到找到一个可用的块组,具体如下:如果我们把开始的块组称为i,那么,该算法要查找的块组号为i mod (n)i+1 mod (n)i+1+2 mod (n)i+1+2+4 mod (n),等等。大家是不是觉得这个东西是不是似曾相识呀,不错,就是伙伴系统算法的思想。

    b.       如果该算法没有找到含有空闲索引节点的块组,就从包含父目录dir的块组开始执行彻底的线性查找,总能找到一个含有空闲索引节点的块组,除非磁盘满了。

     

    find_group_other函数将要分配磁盘索引节点的块组号group,回到ext2_new_inode函数中,下一步要做的事是设置位图。于是调用read_inode_bitmap()得到所选块组的索引节点位图,并从中寻找第一个空闲位,这样就得到了第一个空闲磁盘索引节点号:

     

    static struct buffer_head *

    read_inode_bitmap(struct super_block * sb, unsigned long block_group)

    {

           struct ext2_group_desc *desc;

           struct buffer_head *bh = NULL;

     

           desc = ext2_get_group_desc(sb, block_group, NULL);

           if (!desc)

                  goto error_out;

     

           bh = sb_bread(sb, le32_to_cpu(desc->bg_inode_bitmap));

           if (!bh)

                  ext2_error(sb, "read_inode_bitmap",

                             "Cannot read inode bitmap - "

                             "block_group = %lu, inode_bitmap = %u",

                             block_group, le32_to_cpu(desc->bg_inode_bitmap));

    error_out:

           return bh;

    }

     

    我们看到,read_inode_bitmap函数接收超级块和刚刚我们得到的组号作为参数,通过ext2_get_group_desc函数获得超级快的块组描述符结构:

     

    struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb,

                                            unsigned int block_group,

                                            struct buffer_head ** bh)

    {

           unsigned long group_desc;

           unsigned long offset;

           struct ext2_group_desc * desc;

           struct ext2_sb_info *sbi = EXT2_SB(sb);

     

           if (block_group >= sbi->s_groups_count) {

                  ext2_error (sb, "ext2_get_group_desc",

                             "block_group >= groups_count - "

                             "block_group = %d, groups_count = %lu",

                             block_group, sbi->s_groups_count);

     

                  return NULL;

           }

     

           group_desc = block_group >> EXT2_DESC_PER_BLOCK_BITS(sb);

           offset = block_group & (EXT2_DESC_PER_BLOCK(sb) - 1);

           if (!sbi->s_group_desc[group_desc]) {

                  ext2_error (sb, "ext2_get_group_desc",

                             "Group descriptor not loaded - "

                             "block_group = %d, group_desc = %lu, desc = %lu",

                              block_group, group_desc, offset);

                  return NULL;

           }

     

           desc = (struct ext2_group_desc *) sbi->s_group_desc[group_desc]->b_data;

           if (bh)

                  *bh = sbi->s_group_desc[group_desc];

           return desc + offset;

    }

     

    看前面那个图,Ext2层对超级块对象总是缓存的,整个磁盘的所有ext2_group_desc 都存放在其s_group_desc数组字段对应的buffer_head对应的磁盘高速缓存中,即同样缓存在同一个页面中的,这个函数直接返回块中对应offsetext2_group_desc就行了,有缓存,很简单!

     

    得到了ext2_group_desc之后,read_inode_bitmap函数将其索引节点位图地址对应的那个块缓存到页高速缓存中,最后将该高速缓存描述符buffer_head返回。

     

    接下来ext2_new_inode函数要做的事,是分配磁盘索引节点ext2_inode_info:把索引节点位图中的相应位置位,并把含有这个位图的页高速缓存标记为脏。此外,如果文件系统安装时指定了MS_SYNCHRONOUS标志,则调用sync_dirty_buffer(bitmap_bh)开始I/O写操作并等待,直到写操作终止。

     

    初始化这个ext2_inode_info的字段。特别是,设置嵌入在其内部的inode的索引节点号i_no,并把xtime.tv_sec的值拷贝到i_atimei_mtimei_ctime。把这个块组的索引赋给ext2_inode_info结构的i_block_group字段;初始化这个ext2_inode_info的访问控制列表(ACL);将新索引节点inode对象插入散列表inode_hashtableinsert_inode_hash(inode)调用mark_inode_dirty()把该索引节点的磁盘索引节点对象移进超级块脏索引节点链表;调用ext2_preread_inode()从磁盘读入包含该索引节点的块,将它存入页高速缓存。千万要注意,这里可不是文件预读,而是对磁盘索引节点进行缓存。进行这种所谓的“预读”是因为最近创建的索引节点可能马上会被读写:

     

    static void ext2_preread_inode(struct inode *inode)

    {

           unsigned long block_group;

           unsigned long offset;

           unsigned long block;

           struct buffer_head *bh;

           struct ext2_group_desc * gdp;

           struct backing_dev_info *bdi;

     

           bdi = inode->i_mapping->backing_dev_info;

           if (bdi_read_congested(bdi))

                  return;

           if (bdi_write_congested(bdi))

                  return;

     

           block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);

           gdp = ext2_get_group_desc(inode->i_sb, block_group, &bh);

           if (gdp == NULL)

                  return;

     

           /*

            * Figure out the offset within the block group inode table

            */

           offset = ((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb)) *

                                EXT2_INODE_SIZE(inode->i_sb);

           block = le32_to_cpu(gdp->bg_inode_table) +

                                (offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));

           sb_breadahead(inode->i_sb, block);

    }

    static inline void

    sb_breadahead(struct super_block *sb, sector_t block)

    {

           __breadahead(sb->s_bdev, block, sb->s_blocksize);

    }

     

    我们看到,ext2_preread_inode中首先获得inode对应的块组号block_group,然后通过这个块组号获得对应的ext2_group_desc。得到组描述符后,就可以得到该组对应磁盘索引节点表的磁盘首地址了,然后再根据i_ino得到该磁盘索引节点在表中的位置,赋给内部变量block

     

    通过sb_breadahead读入该索引节点对应磁盘索引节点对象进缓存,最后ext2_new_inode返回新索引节点对象inode的地址。这样,如果想得到i_ino对应的ext2_inode,就可以通过块设备的页高速缓存page->private->b_data获得。

     

    当调用ext2_new_inode函数之后,新的inode和它对应的磁盘索引节点就建好了,并且缓存到了内存中,其体系结构如图:

     

    如图,VFS索引节点号为inoinode,通过函数ext2_preread_inode计数就能得到它对应磁盘索引节点ext2_inode所在的块号block,然后把缓冲区页的描述符插人基树,树根是与块设备相关的特殊bdev文件系统中索引节点的address_space对象。这种缓冲区页必须满足很强的约束条件,就是所有的块缓冲区涉及的块必须是在块设备上相邻存放的,这也是为什么ext2磁盘布局中,所有磁盘索引节点必须连续存放在一个索引节点表中的原因。

     

    还有一点要注意,如果一个页作为缓冲区页使用,那么与它的块缓冲区相关的所有缓冲区首部都被收集在一个单向循环链表中。缓冲区页描述符的private字段指向页中第一个块的缓冲区(由于private字段包含有效数据,而且页的PG_private标志被设置,因此,如果页中包含磁盘数据并且设置了PG_private标志,该页就是一个缓冲区页。注意,尽管如此,其他与块I/O子系统无关的内核组件也因为别的用途而使用privatePG_private字段);每个缓冲区首部存放在b_this_page字段中,该字段是指向链表中下一个缓冲区首部的指针。此外,每个缓冲区首部还把缓冲区页描述符的地址存放在b_page中。

     

    至于__breadahead的底层是怎么实现的,对于块设备的页高速缓存(注意与后面的普通文件页高速缓存相区别)的详细内容,请访问博客“把块存放在页高速缓存中” http://blog.csdn.net/yunsongice/archive/2010/08/30/5850656.aspx

     

    展开全文
  • OOC 面向对象C语言编程实践

    千次阅读 2016-07-23 17:10:57
    面向对象是一种编程思想,虽然C并没有提供面向对象的语法糖,但仍然可以用面向对象的思维来抽象和使用。这里分享一套C面向对象的写法,可以完成面向对象编程并进行流畅的抽象。这套写法是在实践中不断调整的结果,...

        面向对象是一种编程思想,虽然C并没有提供面向对象的语法糖,但仍然可以用面向对象的思维来抽象和使用。这里分享一套C面向对象的写法,可以完成面向对象编程并进行流畅的抽象。这套写法是在实践中不断调整的结果,目前已经比较稳定,进行了大量的功能编写。


         这套OOC有以下特性:

    • 没有强行去模仿c++的语法设定,而是使用C的特性去面向对象设计
    • 实现继承,组合,封装,多态的特性
    • 一套命名规范去增加代码的可读性

          第一,封装
          在C中可以用struct来封装数据,如果是方法,我们就需要用函数指针存放到struct里面来模拟。
    typedef struct Drawable Drawable;
    struct  Drawable
    {
    	 float positionX;
    	 float positionY;
    };
    typedef struct {
    	Drawable* (*Create)                ();
    	void      (*Init)                  (Drawable* outDrawable);
    }
    _ADrawable_;
    
    extern _ADrawable_ ADrawable[1];
    static void InitDrawable(Drawable* drawable)
    {
         drawable->positionX = 0.0f;
         drawable->positionY = 0.0f;
    }
    
    static Drawable* Create()
    {
    	Drawable* drawable = (Drawable*) malloc(sizeof(Drawable));
    	InitDrawable(drawable);
    
    	return drawable;
    }
    
    static void Init(Drawable* outDrawable)
    {
    	InitDrawable(outDrawable);
    }
    
    _ADrawable_ ADrawable[1] =
    {
    	Create,
    	Init,
    };
    • 数据我们封装在Drawable结构里,通过Create可以再堆上创建需要自己free,Init是在栈上创建
    • 函数封装在ADrawable这个全局单例对象里,由于没有this指针,所有方法第一个参数需要传入操作对象
    • Create和Init方法将会管理,对象的数据初始化工作。如果对象含有其它对象,就需要调用其Create或Init方法

        第二,继承和组合
    typedef struct Drawable Drawable;
    struct  Drawable
    {
    	 Drawable* parent;
    	 Color     color[1];
    };
    • 继承,就是在结构体里,嵌入另一个结构体。这样malloc一次就得到全部的内存空间,释放也就一次。嵌入的结构体就是父类,子类拥有父类全部的数据空间内容。
    • 组合,就是在结构体,存放另一个结构体的指针。这样创建结构体时候,要需要调用父类的Create方法去生成父类空间,释放的时候也需要额外释放父类空间。
    • 这里parent就是组合,color就是继承。
    • 继承是一种强耦合,无论如何子类拥有父类全部的信息。
    • 组合是一种低耦合,如果不初始化,子类只是存放了一个空指针来占位关联。
    • 可以看到,C里面一个结构体可以,继承任意多个父类,也可以组合任意多个父类。
    • color[1] 使用数组形式,可以直接把color当做指针使用
            子类访问父类,可以直接通过成员属性。那么如果通过父类访问子类呢 ?  通过一个宏定义来实现这个功能。
    /**
     * Get struct pointer from member pointer
     */
    #define StructFrom2(memberPtr, structType) \
    	((structType*) ((char*) memberPtr - offsetof(structType, memberPtr)))
    
    
    /**
     * Get struct pointer from member pointer
     */
    #define StructFrom3(memberPtr, structType, memberName) \
    	((structType*) ((char*) memberPtr - offsetof(structType, memberName)))
    
    
    typedef struct Sprite Sprite;
    struct  Sprite
    {
    	Drawable drawable[1];
    };
    Sprite* sprite = StructFrom2(drawable, Sprite);

         这样,我们就可以,通过Sprite的父类Drawable属性,来获得子类Sprite的指针。其原理,是通过offsetof获得成员偏移量,然后用成员地址偏移到子类地址上。有了这个机制,我们就可以实现多态,接口等抽象层。

         我们可以在接口函数中,统一传入父类对象,就可以拿到具体的子类指针,执行不同的逻辑,让接口方法体现出多态特性。


          第三, 多态
    typedef struct Drawable Drawable;
    struct  Drawable
    {
    	 /** Default 0.0f */
    	 float     width;
    	 float     height;
    
    	 /**
    	  * Custom draw called by ADrawable's Draw, not use any openGL command
    	  */
    	 void (*Draw)  (Drawable* drawable);
    };
        当,我们把一个函数指针放入,结构体对象的时候。意味着,在不同的对象里,Draw函数可以替换为不同的实现。而不是像在ADrawable里面的函数只有一个固定的实现。在子类继承Drawable的时候,我们可以给Draw赋予具体的实现。而统一的调用Draw(Drawable* drawable)的时候,就会体现出多态特性,不同的子类有不懂的实现。
    typedef struct
    {
        Drawable drawable[1];
    }
    Hero;
    typedef struct
    {
        Drawable drawable[1];
    }
    Enemy;
    Drawable drawables[] = 
    {
         hero->drawable,
         enemy->drawable,
    };
    
    for (int i = 0; i < 2; i++)
    {
        Drawable* drawable = drawables[i];
        drawable->Draw(drawable);
    }

         在Hero和Enemy的Create函数中,我们分别实现Draw(Drawable* drawable)函数。如果,我们有一个绘制队列,里面都是Drawable对象。传入Hero和Enemy的Drawable成员属性。在统一绘制调用中,drawable->Draw(drawable),就会分别调用Hero和Enemy不同的draw函数实现,体现了多态性。

       

        第四,重写父类方法

        在继承链中,我们常常需要重写父类方法,有时候还需要调用父类方法。
    typedef struct
    {
        Sprite sprite[1];
    }
    SpriteBatch;
         比如,SpriteBatch 继承 Sprite, 我们需要重写Draw方法,还需要调用Sprite的Draw方法。那么我们就需要把Sprite的Draw方法公布出来。
    typedef struct
    {
       Drawable drawable[1];
    }
    Sprite;
    
    typedef struct
    {
        void (*Draw)(Drawable* drawable);
    }
    _ASprite_;
    
    extern _ASprite_ ASprite;
          这样,每个Sprite的Draw方法可以,通过ASprite的Draw访问。
    // override
    static void SpriteBatchDraw(Drawable* drawable)
    {
          // call father
          ASprite->Draw(drawable);
    }
    
    spriteBatch->sprite->drawable->Draw = SpriteBatchDraw;
         那么,SpriteBatch就重写了父类的Draw方法,也能够调用父类的方法了。


        第五,内存管理
        一个malloc对应一个free,所以Create出来的对象需要自己手动free。关键是,在于组合的情况。就是对象内有别的对象的指针,有些是自己malloc的,有些是共用的。其实,计数器是一个简单的方案,但我仍然觉得复杂了。在想到更好的方案之前,我倾向于更原始的手动方法,给有需要内存管理的对象添加Release方法。

    typedef struct
    {
        Drawable* parent;
        Array*    children;
    }
    Drawable;
    
    typedef struct
    {
        void (*Release)(Drawable* drawable);
    }
    _ADrawable_;
    
    extern _ADrawable_ ADrawable;

        Drawable 含有两个指针, 一个是parent可能别的对象也会使用,所以这个parent在Release函数中不能确定释放。还有一个children这个数组本身是可以释放的,所以在Create函数里,我们自己malloc的,都要在Release方法里自己free。

         所以,对于Create方法我们需要free + Release。对于Init 只需要调用Release方法就可以释放完全了。那么,parent这种公用的指针,就需要paren对象自己在合适的时机去释放自己。肯定没有计数器来的方便,但是这个足够简单开销也很小。
         
    展开全文
  • 将MathType6嵌入word2016

    千次阅读 2020-04-13 10:50:35
    打开word选择创建空白文件,然后在菜单栏找到视图,点击,找到,录制,在录制中可以对宏明进行更改,如下如: 点击插入,找到对象,在新建中找到MathType6.0 Equation,然后点击确定 接下来会跳出...

    安装教程:

    • 打开word选择创建空白文件,然后在菜单栏找到视图,点击,找到宏,录制宏,在宏录制中可以对宏明进行更改,如下如:

    在这里插入图片描述

    • 点击插入,找到对象,在新建中找到MathType6.0 Equation,然后点击确定

    在这里插入图片描述

    • 接下来会跳出MathType6.0主程序,再次进入,选择暂停录制

    在这里插入图片描述

    • 点击文件选项自定义功能区,按照下图步骤操作,其中新建的选项卡自己可以重命名,完成后就可在word中嵌入成功了

    在这里插入图片描述

    使用中可能存在的问题:

    问题描述:

    用于创建此对象的程序是Equation。您的计算机尚未安装此程序或此程序无响应。若要编辑此对象,请安装Equation或确保Equation中的任何对话框都已关闭。

    在这里插入图片描述

    解决方法:

    • 出现这的问题,在网上看到有人是通过删除windows下最近更新的字体解决的,想了解的可以搜一下。
    • 我通过点击文件夹中的MathType.exe,它提示我缺少某种可编辑字体,让我卸载后重新安装。按照提示我对MathType进行了卸载和再安装,最后问题得以解决。
    展开全文
  • VS嵌入汇编ASM

    万次阅读 2012-09-18 20:48:46
     C++语言是C语言的超集,它是在C语言的基础上扩展形成的面向对象程序设计语言。微软Visual C++ 5.0∕6.0则是Windows 9.x平台上广泛应用的开发系统。本节以Visual C++ 5.0∕6.0为例,说明32位Windows 9.x环境下汇编...
  • 本文档提供了一个JavaScript(JS)引擎的C语言实现的概述,他介绍了你如何在你的应用程序中嵌入脚本引擎来让它们可以使用JS。有两大理由让你在应用程序中嵌入JS引擎:使用脚本来自动操作你的应用程序;同时使用JS...
  • haxe的 条件编译以及

    千次阅读 2015-04-07 21:00:46
    可以扫描资源文件夹,用于自动嵌入文件或者 IDE 智能提示.  条件编译 条件编译 (Conditional Compilation). 通过使用 #if #else #elseif #end 来 检测编译器标志 编译标志(define), 通过使用 -D key=...
  • Excel教程 (的介绍与基本使用)

    万次阅读 2013-06-09 08:39:25
    Excel教程 (的介绍与基本使用) Microsoft excel是一款功能非常强大的电子表格软件。它可以轻松地完成数据的各类数学运算,并用各种二维或三维图形形象地表示出来,从而大大简化了数据的处理工作。但若仅...
  • 病毒就是Word中被嵌入的带有恶意行为的代码(VBA代码),当打开带有病毒的word文档时,嵌入其中的代码会自动运行 Word 将下列名称识别为自动,或称“auto”,当相应动作被执行,会自动调用满足条件的VBA...
  • 在CDockablePane中嵌入目录

    千次阅读 2013-01-08 16:34:18
    1.首先MFC向导生成一个单文档项目. 2.使用类向导从CDockablePane中派生出一个类.这里起名为CFileView 并且在stdafx.h中添加 #include "FileView.h" 3.在主框架类中(MainFrame.h)添加...4.创建浮动窗口在MainFrane
  • ====注意:所有新的开发都在http://github.com/rowanthorpe/define-ext上继续进行,可以在此处找到较新的版本。... 使用现有的作为模板的其他编译语言(例如Fortran或ECL)的新插件应该很容易创建
  • C++嵌入DLL到资源运行释放的问题

    千次阅读 2017-09-16 18:29:08
    以前写过一篇博文C#嵌入dll到资源释放的问题。虽然相对于C#,C++中嵌入DLL到程序资源中,然后再释放出来的应用场合并没有那么多,但是还是有必要了解下一般的过程。结合本人在实际工作中解决此类问题的实践思路,...
  • Racket编程指南——16 (macro)

    千次阅读 2018-02-20 23:30:37
    (macro)是一种语法表,它有一个关联的转换器(transformer),它将原有的表扩展(expand)为现有的表。换句话说,是Racket编译器的扩展。racket/base和racket的大部分句法表实际上是,扩展成一小部分核心...
  • C/C++ 多线程调用嵌入Python完整流程

    千次阅读 2018-08-26 00:25:16
    C/C++ 多线程调用嵌入Python 最近都很忙,忙着把公司的Python回测框架完成。前两天公司同事抱怨 C/C++调用Python超级烦人,动不动就返回NULL,而且内存暴涨,于是我决定尝试解决这个问题,提供一套完整的开发流程,...
  • JavaScript-C引擎嵌入开发指南

    千次阅读 2008-09-18 20:06:00
    导读: JavaScript-C引擎概览 本文档提供了一个JavaScript(JS)引擎的C语言实现的概述,他介绍了你如何在你的应用程序中嵌入脚本引擎来让它们可以使用JS。有两大理由让你在应用程序中嵌入JS引擎:使用脚本来自动...
  • 如何 在C 程序中嵌入Perl

    千次阅读 2011-01-06 15:41:00
    嵌入非 Perl 代码时不必再有使用 XS 的烦恼 Michael Roberts (michael@vivtek.com), Owner, Vivtek Michael Roberts自成年以来一直都把时间花在编码上(小孩,青年时代也是如此),但开始撰写有关这方面的文章...
  • (C++)用模板或实现单例模式

    千次阅读 2015-02-22 22:07:45
    在cocos2d-x开发中经常会用到单例模式,而每个单例模式类实际上具备相似的基础结构,为了便于快速写出一个具有单例模式的类,可以借助模板或者。 1.单例模式的类实现 2.单例模式的模板实现 3.单例模式的实现 4....
  • C++面向对象(一)——类与对象

    千次阅读 2016-04-28 21:30:27
    C++面向对象(一)——类与对象
  • 译;将Python嵌入到Qt程序中

    万次阅读 2010-06-02 15:03:00
    翻译Qt Quarterly的一篇文章: 利用PythonQt将Python嵌入到Qt程序中
  • 进阶(三)对象树与拥有权 楼主  发表于 2013-9-12 16:39:33 | 查看: 255| 回复: 1 对象树与拥有权 版权声明 该文章原创于Qter开源社区 导语 ...
  • perlembed - 在 C 程序中嵌入 perl

    千次阅读 2010-01-29 22:01:00
    NAME perlembed - 在 C 程序中嵌入 perlDESCRIPTION 导言 你是想要: 在 Perl 中使用 C? 阅读 perlxstut、perlxs、h2xs、perlguts 和 perlapi。 在 Perl 中使用 Unix 程序? 阅读反引用符(back-quote)和 L...
  • [CodeComplete]创建一个函数需要理由吗

    千次阅读 2012-06-27 07:57:40
    以下为代码大全2>>[第七章高质量的子程序]的摘录 编程中什么是标准,相信大家都没有办法给出一套成系统的理论,而《代码大全》的作者就是在为我们描述... 创建子程序的正当理由  在子程序层上展开设计  起个好名字
  • Excel 对象模型

    2016-11-11 10:54:43
    Excel 对象模型
  • 创建有个性的对话框之ATL/WTL篇

    万次阅读 2005-09-19 20:10:00
    输入您的搜索字词 提交搜索表单 前记 这几个嵌入类其实很早之间就完成了,2003年的时候我在CodeProject上发布了这些代码,不过当时使用了紫色作为按钮的边框,导致几个无聊的LY在哪里争吵
  • VC 定义一

    千次阅读 2005-03-29 09:47:00
    AND_CATCHAND_CATCH AND_CATCH(exception_...使用CATCH以获得一个异常类型,然后使用 AND_CATCH获得随后的异常处理代码可以访问异常对象(若合适的话)已得到关于异常的特别原因的更多消息。在AND_CATCH块中调用
  • 在MFC应用程序中动态嵌入Word文档

    千次阅读 2009-02-06 11:20:00
    摘要:本文通过对自动化等COM技术的应用介绍了一种在MFC应用程序中动态嵌入Microsoft Word文档的简单方法。使在MFC应用程序中即可打开、显示和存储外部Word文档。 关键字:VC++、MFC、COM、自动化引言 Microsoft ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 13,530
精华内容 5,412
关键字:

创建嵌入宏对象